I'm going over SICP translating problems into Clojure to learn both Clojure and read SICP. Currently, I am stuck with the Count Leaves procedure from Section 2.2.2.
The goal is to write a function that takes a list representation of a tree, e.g. '(1 2 '(3 4)) and counts the number of leaves, in this case 4.
So far, the closest I have come up with is
(defn count-leaves
[coll]
(cond
(nil? coll) 0
(not (seq? coll)) 1
:else (let [[left & right] coll] (+ (count-leaves left) (count-leaves right)))
))
However, this does not handle subtrees correctly. In particular, it evaluates
(count-leaves '('(1)))
to 2 instead of 1.
Note the Scheme implementation from the book is:
(define (count-leaves x)
(cond ((null? x) 0)
((not (pair? x)) 1)
(else (+ (count-leaves (car x))
(count-leaves (cdr x))))))
Comment
As @jkiski's comment suggests, your code works. So there is no problem.
But I'd prefer to test whether the argument is a sequence first. Try working out how
(count-leaves '())
evaluates to0
!Switch the first two clauses of the
cond
and we get ...... where I've used
rest
instead of thenext
implicit in the destructuring, soempty?
instead ofnil?
to test it. This deals properly withnil
values, which your code doesn't. But it is still properly recursive, so remains subject to stack overflow.I prefer ...
... which is still recursive, but cleaner.
Edit
I've had to retract my good opinion of @glts's solution:
postwalk
is recursive, so offers no real advantage.