Search code examples
listrecursionschemeletrun-length-encoding

Counts of repeated elements in a list


This program takes a list where elements are repeated, e.g L = (a a a b b b c c c d), and output a list of items and number of repetition e.g ((a 3)(b 3)(c 3) d)

(define counter 0)
(define (compress liste)
(if (or (null? liste) (null? (cdr liste)))
   liste
(let ((compressed-cdr (compress (cdr liste)))) 
   (if (equal? (car liste) (car compressed-cdr))
   ((+ counter 1) compressed-cdr)
  ((cons (car liste) counter) (= counter 0) (compressed-cdr))))
                       ))

However, I get this error:

Error: application: not a procedure; expected a procedure that can be applied to arguments
given: 1 arguments ...

The error is at the true predicate of the second if condition.


Solution

  • Building the result list in a top-down manner, with the "head-sentinel trick", for simplicity:

    (define (rle lst)
      (if (null? lst) 
       '()
       (let ((res (list 1)))        ; head sentinel
          (let loop ((p   res)      ; result's last cons cell  
                     (elt (car lst))
                     (cnt 1) 
                     (lst (cdr lst)))
             (if (and (not (null? lst))
                      (equal? elt (car lst)))
                (loop p elt (+ cnt 1) (cdr lst))
                (begin
                   (set-cdr! p (list (if (= 1 cnt) elt (list elt cnt))))
                   (if (null? lst)
                      (cdr res)     ; skip the head in result, on return
                      (loop (cdr p) (car lst) 1 (cdr lst)))))))))
    

    As @uselpa explained, this is called run-length encoding; for the uniformity of the result I'd suggest using (x 1) representation for non-repeating elements.

    And the error "Error: application: not a procedure; expected a procedure", as others have said, means that the system expected to find a procedure but found something else, so can't apply it. Scheme expects to find a procedure as the first form in a list: (proc args ...), and tries to apply it to the arguments. But in your code it is not a procedure, but some other type of data.


    If your Scheme has left fold, or reduce, you can run through it twice - first collecting the uniform results, and then applying your special format while reversing (left fold's results are usually built in reversed order):

    (define (fold f init lst)            ; if fold is not defined,
       (reduce f init (cons init lst)))  ;   define it in terms of reduce
    
    (define (rle lst)
       (fold (lambda (x acc)             ; NB! MIT-Scheme: (acc x)
                 (if (= 1 (cadr x))  (cons (car x) acc)  (cons x acc)))
             '()
          (fold (lambda (x acc)          ; NB! MIT-Scheme: (acc x)
                    (if (or (null? acc) (not (equal? (caar acc) x)))
                       (cons (list x 1) acc)
                       (cons (list x (+ (cadar acc) 1)) (cdr acc))))
                '() 
                lst)))