2

So I'm coding in Lisp and I came up with a function that counts the number of atoms in a list (with no sub-lists). so the code is this:

(defun num-atoms (list)
  (cond
    ((null list) 0)
    ((atom list) 1)
    (t (+ (num-atoms (car list))
          (num-atoms (cdr list))))))

and this works and makes sense to me. So if I call this function with the list (1 2 3) in argument it should go as follows:

  • (num-atoms '(1 2 3))
  • not null
  • not atom
  • num-atoms(1)
  • atom so returns 1
  • num-atoms ((2 3))
  • not null
  • not atom
  • num-atoms (2)
  • return 1
  • ....
  • and so on

BUT, if I write the code like this:

(defun num-atoms (list)
  (cond
    ((null list) 0)
    ((atom list) 1)
    (t (+ (num-atoms (cdr list))
          (num-atoms (car list))))))

here I just switched de car and cdr positions in the last line.

It still works ! and if I do a Trace, it gives me the number of atoms in the same order ! it counts the 1 first in the '(1 2 3) list and so on. Can someone explain to me how this 2nd version of the code still works ? I dont really understand the logic here.

3
  • Not an answer, but, you should be aware that (count-if #'atom …) would do this. FWIW. Commented Nov 2, 2015 at 16:25
  • 1
    @BRPocock As far as I see, that is wrong. count-if would count all top-level atoms in a sequence, whereas his version counts all atoms in a tree. Commented Nov 3, 2015 at 7:32
  • I'm assuming “list with no sub-lists” to not be a tree, but, point taken. Commented Nov 10, 2015 at 21:31

1 Answer 1

4

If you trace both functions you'll see how hey differ.

In your first version, the atoms get processed in order because the function processes the first element (car) and then the remaining elements (cdr).

In the second version, the atoms get processes in reverse order because the function first processes the remaining elements before processing the first element. So the first atom that is found is the last in the list, and then the stack unwinds.

But of course the result is the same in both cases since addition is commutative.

* (defun num-atoms(list)
  (cond
   ((null list) 0)
   ((atom list) 1)
   (t (+ (num-atoms (car list)) (num-atoms (cdr list))))))

NUM-ATOMS
* (trace num-atoms)

(NUM-ATOMS)
* (num-atoms '(1 2 3))
  0: (NUM-ATOMS (1 2 3))
    1: (NUM-ATOMS 1)
    1: NUM-ATOMS returned 1
    1: (NUM-ATOMS (2 3))
      2: (NUM-ATOMS 2)
      2: NUM-ATOMS returned 1
      2: (NUM-ATOMS (3))
        3: (NUM-ATOMS 3)
        3: NUM-ATOMS returned 1
        3: (NUM-ATOMS NIL)
        3: NUM-ATOMS returned 0
      2: NUM-ATOMS returned 1
    1: NUM-ATOMS returned 2
  0: NUM-ATOMS returned 3
3

and

* (defun num-atoms(list)
  (cond
   ((null list) 0)
   ((atom list) 1)
   (t (+ (num-atoms (cdr list)) (num-atoms (car list))))))

NUM-ATOMS
* (num-atoms '(1 2 3))
  0: (NUM-ATOMS (1 2 3))
    1: (NUM-ATOMS (2 3))
      2: (NUM-ATOMS (3))
        3: (NUM-ATOMS NIL)
        3: NUM-ATOMS returned 0
        3: (NUM-ATOMS 3)
        3: NUM-ATOMS returned 1
      2: NUM-ATOMS returned 1
      2: (NUM-ATOMS 2)
      2: NUM-ATOMS returned 1
    1: NUM-ATOMS returned 2
    1: (NUM-ATOMS 1)
    1: NUM-ATOMS returned 1
  0: NUM-ATOMS returned 3
3
*
Sign up to request clarification or add additional context in comments.

1 Comment

thanks for your answer ! i understand what u tried to explain to me, it all makes sense now. What really confused me was how the lisp processed the code ( the order) but i think i got it now !

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.