Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Complex Example: Run-Length Encoding

Let’s implement a simple run-length encoder using binary comprehensions. This algorithm compresses sequences of repeated values:

(defun rle-encode (data)
  "Simple run-length encoding for byte data."
  (rle-encode-helper data '()))

(defun rle-encode-helper
  ([(binary ()) acc]
   (pack-rle-pairs (lists:reverse acc)))

  ([(binary ((byte (size 8)) rest binary)) acc]
   (let ((count-and-rest (count-bytes byte rest 1)))
     (rle-encode-helper (element 2 count-and-rest)
                       (cons `#(,byte ,(element 1 count-and-rest)) acc)))))

(defun count-bytes (byte data count)
  "Count consecutive occurrences of byte."
  (case data
    ((binary (byte rest binary))
     (count-bytes byte rest (+ count 1)))
    (_
     `#(,count ,data))))

(defun pack-rle-pairs (pairs)
  "Pack RLE pairs into binary."
  (binary-comp ((<<(byte (size 8)) (count (size 8))>>
                 (list-gen (<- pairs))))
               (tuple byte count)))

Testing this contraption:

lfe> (set input #B(65 65 65 65 66 66 67 68 68 68 68 68))
#B(65 65 65 65 66 66 67 68 68 68 68 68)  ; AAAABBCDDDDD

lfe> (rle-encode input)
#B(65 4 66 2 67 1 68 5)  ; A:4, B:2, C:1, D:5

Each pair of bytes in the output represents (value, count). This is more efficient when you have long runs of repeated values, though admittedly less efficient when you don’t—rather like how a towel is useful when you’re wet but somewhat burdensome when you’re trying to pack light.