Search code examples
lisprepl-printed-representation

Count occurrences in lisp


I'm trying to make a code in lisp to count occurrences of atoms in a list in lisp. The problem is the code works for all atoms except the atom (), which appears as NIL. Example in the code:

(defun flatten (list_) 
    (cond   ((atom list_) (list list_))
            ((null list_) NIL)
            (t (append (flatten (car list_)) (flatten (cdr list_))) )
    )
)

(defun toUniqueList (list_ out) 
    (cond   ((null list_) NIL)
            ((not (member (car list_) out)) (append (list (car list_)) (toUniqueList (cdr list_) (append (list (car list_)) out)) ))
            (t (toUniqueList (cdr list_) out))
    )
)

(defun countOccurences (list_ x) 
    (cond   ((null list_) 0)
            ((eql (car list_) x) (+ (countOccurences (cdr list_) x) 1))
            (t (countOccurences (cdr list_) x))
    )

)

(defun countOccurencesAll (list_) 
    (setq flatList (flatten list_))
    (setq parsed (toUniqueList flatList '()))
    (setq result '())
    (dolist (x parsed)
        (setq result (append result (list (list x (countOccurences flatList x)) ))))
    result
)

(write (countOccurencesAll '(x y z 4.6 (a x) () (5 z x) ())))
; ((X 3) (Y 1) (Z 2) (4.6 1) (A 1) (NIL 5) (5 1))

Any idea in how to show () rather than NIL?


Solution

  • The expressions nil, 'nil, (), and '() all gets evaluated to nil which is displayed as nil unless it is the cdr of a pair in which it will just close the list. eg. '(() . ()) gets evaluated to (NIL . NIL) and it is displayed as (NIL). There is nothing you can do about that.

    So the question is then, because ((a) (()) (c)) is really ((a . nil) . ((nil . nil) . ((c . nil) . nil))) should it count nil/() 5 times or ignore when nil in the cdr of a pair and just count it as one?

    BTW using setq in countOccurencesAll on undefined bindings means your code is in the mercy of the implementation. The hyperspec does not define how it should be handled and SBCL makes warnings about how it interprets the code and other might just choose an interpretation. A better approach would be to use let to define the bindings. Using a hash and iterate over the list once would make an O(n) solution.