I am trying to write a weird function, so bear with me here. This function should take a list L
as a parameter and have a sum
variable. If L
is not a list, it should return nil
. Otherwise, it should iterate through each element of the list and do the following:
- If the element is a number and less than zero, it should subtract 1 from the sum.
- If the element is a number and greater than zero, it should add 1 to the sum.
- if the element is 0 or not a number, then it should add 0 to the sum.
Here's the code I have, but it returns 0 regardless of arguments passed in:
(defun sigsum (L)
(let ((sum 0)) ;;variable sum
(if (not (listp L)) ;;if L is not a list
nil ;;return nil
(dotimes (i (length L)) ;;otherwise loop through L
(if (numberp (elt L i)) ;;if each element is a number
(if (< (elt L i) 0) ;;if is less than 0 subtract 1 from sum
(- sum 1)
(if (> (elt L i) 0) ;;if greater than 0 add 1 to sum
(+ sum 1))
(+ sum 0)) ;;else add 0 to sum
(+ sum 0))) ;;not a number so add 0 to sum
)
sum) ;;return sum
)
As always, any help is greatly appreciated.
Usage:
(discrete '(-1 2 3 0 2 2 -1 2 34 0 -1))
=> 3(discrete '(-4 a b))
=> -1(discrete '())
=> NIL(discrete '(a s d f))
=> 0The expression
(+ sum 1)
returns one more thansum
. It does not changesum
. You can get where you want usingincf
ordecf
, which modify places.You could also use
loop
:Signum
is also useful, but it has float contagion (if any of the numbers are floats, the result will be a float as well).Other answers have described the problems in your code, but it might be beneficial to look at other ways of solving the problem. This is a pretty typical case for a reduction with a key function (see reduce). You can sum up the elements in list with (reduce '+ list). However, you don't want to just sum up the elements, some of which might not be numbers, you want to map each element to a number (-1, 0, or 1), and add those up. That means you need a key function. First, let's define the function that takes an element to either -1, 0, or 1:
Then your sum function needs to return nil if its argument is not a list, or (reduce '+ … :key 'to-number) if its argument is a list:
Conceptually, this approach is the same as applying the addition operator to the result of (mapcar 'to-number list), but reduce is generally preferred, because there can be a maximum number of arguments a function can be called with, so (apply '+ (mapcar …)) breaks if (mapcar …) returns a list that's longer than that. The other problem is that mapcar will allocate a whole new list to hold the intermediate values (the results of to-number), which is an unnecessary space usage.
(- sum 1)
does not update any variables. Since you don't use the result it goes away. It's like this is all programming languages.sum + 1
in an Algol language (like C) does not updatesum
.If you want to update a variable you can use
setf
:Or you can use
incf
anddecf
that are macroes that expand to the same as the above expressions.There are many other ways you can do it in CL. You can use reduce
You can use loop: