#-(and) "

P10 (*) Run-length encoding of a list.
    Use the result of problem P09 to implement the so-called run-length encoding data compression method. Consecutive duplicates of elements are encoded as lists (N E) where N is the number of duplicates of the element E.

    Example:
    * (encode '(a a a a b c c a a d e e e e))
    ((4 A) (1 B) (2 C) (2 A) (1 D)(4 E))

"


;; Nice recursive solution, using the same design pattern as in P09:

(defun encode (list)
  (labels ((encode-run (element count list)
             (cond
               ((null list) (list (list count element)))
               ((eql element (first list)) (encode-run element (1+ count) (rest list)))
               (t (cons (list count element) (encode-run (first list) 1 (rest list)))))))
    (if (null list)
        '()
        (encode-run (first list) 1 (rest list)))))


;; Nice, functional solution, reusing functions from p09, uses O(n+r) space:

(defun encode (list)
  (mapcar (lambda (group) (list (length group) (first group)))
          (group list)))


;; Smartass solution, using Common Lisp reduce:

(defun encode (list)
  (reduce (lambda (item result)
            (print (list item result))
            (cond
              ((endp result)                      (list (list 1 item)))
              ((eql (second (first result)) item) (cons (list (1+ (first (first result))) item)
                                                        (rest result)))
              (t                                  (cons (list 1 item) result))))
          list
          :from-end t
          :initial-value '()))


;; Smartass solution, using Common Lisp reduce, and a closure; more
;; efficient since we don't call cons twice at each step, but
;; inelegant, since we have to add the last result as an afterthought:

(defun encode (list)
  (when list
    (let ((count     0)
          (last-item nil))
      (let ((tail-result (reduce (lambda (item result)
                                   (cond
                                     ((zerop count)
                                      (setf count 1
                                            last-item item)
                                      result)
                                     ((eql item last-item)
                                      (incf count)
                                      result)
                                     (t
                                      (prog1 (cons (list count last-item) result)
                                        (setf count 1
                                              last-item item)))))
                                 list
                                 :from-end t
                                 :initial-value '())))
        (cons (list count last-item) tail-result)))))


;; Iterative solution, uses only O(r) space:

(defun encode (list)
  (when list
    (loop
       :with count = 0
       :with last-item = nil
       :with result = '()
       :for item :in list
       :do (cond
             ((zerop count)        (setf count 1
                                         last-item item))
             ((eql item last-item) (incf count))
             (t                    (push (list count last-item) result)
                                   (setf count 1
                                         last-item item)))
       :finally (when (plusp count)
                  (push (list count last-item) result))
       (return (nreverse result)))))


;;;; THE END ;;;;
ViewGit