I'd like to find a more elegant solution to the following.
Below appears correct, but it's hard to read.
Is my first use of the map function below poor in stylistic terms?
It feels that way, and I'd like to pin down why.
I wonder what is a nicer way to achieve this.
;; ANSI Common Lisp ex 3.3, p56.
;; "Define a function that takes a list and returns a list indicating
;; number of times each (eql) element appears, sorted from most common
;; element to least common"
;; > (occurrences '(a b a d a c d c a))
;; ((A . 4) (C . 2) (D . 2) (B . 1))
(defun occurrences (lst)
(labels ((build-flist (lst res) ; [1]
(if (null lst)
res
(build-flist (cdr lst) (mapcar #'(lambda (x) (if (eql (car x) (car lst)) ; [2]
(cons (car x) (1+ (cdr x))) ; [3] ; rtn inc'd freq entry
x)) ; rtn unchanged freq entry
res))))
(remove-dups (lst) ; [4]
(if (null lst)
nil
(adjoin (car lst)
(remove-dups (cdr lst))))))
(sort (build-flist lst (mapcar #'(lambda (x) (cons x 0)) (remove-dups lst))) ; [5]
#'> :key #'cdr)))
;;[1] build-flist builds a frequency list. it is called like:
;; (build-flist '(a b a d a c d c a) '((A . 0) (B . 0) (C . 0) (D . 0))
;;[2] map over entire res assoc list, incrementing frequency if it equals
;; the el just removed from head of list, else just keep entry as is (inefficient).
;;[3] inelegant. in map, is there some kind of when form. ;; <<< STILL TENSION HERE, CODE IS HARD TO READ
;;[4] remove-dups uses adjoin, (set-based operator). this will help us ;; --------------------------------------------
;; build an zero-initialised freq list, to get us started.
;;[5] Finally, our actual function builds the frequency list, then sorts the result.
;; We pass it a frequency list initialised to zero to get started.
;; Discussion
;; It doesn't always help to encapsulate helper functions in labels.
;; If the functions are simple, perhaps they could be global, if they're complex, perhaps they could be simplified.
```