0

I need to create a recursive method in LISP that takes the numbers in a list and finds the sum. Anything in the list that is not a number is skipped (For example, if the list contains "Cheese 12 Dog 8 Shoe 5", the output would be 25).

Right now my code finds the sum, but throws an error if there is anything in the list that is not a number. What can be changed to fix that?

(defun adder (lis)
   (cond
    ((null lis) 0)
    (t (eval (cons '+ lis)) )
  )
)
  • 1
    `numberp` determines whether an item is a number... Something like `...(cons '+ (map 'list #'(lambda (x) (if (numberp x) x 0)) lis))`... – abiessu Nov 12 '13 at 18:17

2 Answers2

2

This would do:

(defun adder (lis)
  (if (null lis)
    0
    (let ((c (car lis)))
      (if (numberp c)
        (+ c (adder (cdr lis)))
        (adder (cdr lis))))))

Your version is not recursive (you don't call adder inside of adder), maybe you meant something like this (which is non-recursive)?

(defun adder (lis)
  (apply '+ (remove-if-not 'numberp lis)))
uselpa
  • 18,732
  • 2
  • 34
  • 52
2

Using apply on lists that can be long is a bit dangerous. If the list is longer than call-arguments-limit, then (apply '+ list) won't work. Now, call-arguments-limit is typically pretty big in modern Lisps, but it's allowed to be as small as 50. For more information about this, see:

I think your best bet would be to use reduce '+ list with a key function that takes each number to itself and each non-number to 0. (This key function is what abiessu mentioned in a comment.)

(reduce '+ list :key (lambda (x) (if (numberp x) x 0)))
CL-USER> (let ((list '(cheese 12 dog 8 shoe 5)))
           (reduce '+ list :key (lambda (x) (if (numberp x) x 0))))
25
CL-USER> (let ((list '()))
           (reduce '+ list :key (lambda (x) (if (numberp x) x 0))))
0

Instead of using a more complex key function, you could also use (remove-if-not 'numberp list) to get rid of the non-numbers (or (remove-if (complement 'numberp) list)):

CL-USER> (let ((list '(cheese 12 dog 8 shoe 5)))
           (reduce '+ (remove-if-not 'numberp list)))
25
CL-USER> (let ((list '()))
           (reduce '+ (remove-if-not 'numberp list)))
0
Community
  • 1
  • 1
Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
  • I had forgotten the simpler ways of writing the `reduce` and `remove-*` operations, thanks for the reminder. :-) – abiessu Nov 12 '13 at 19:31
  • @abiessu Well, I do think that this way is simpler, but more importantly, it's _safer_, because if your list is longer than [`call-arguments-limit`](http://www.lispworks.com/documentation/HyperSpec/Body/v_call_a.htm) then `(apply '+ list)` won't work, where as `(reduce '+ list)` is still OK. – Joshua Taylor Nov 12 '13 at 20:36