trev66
trev66

Reputation: 1

to apply #' or not to apply #'

I've just started to play around with Lisp (Common-Lisp), here's a function that calculates the average of a list of numbers

CL-USER> (defun average (list) (/ (apply #'+ list) (length list)))
AVERAGE
CL-USER> (average '(1 2 3 4))
5/2

however if I rewrite the function like so

CL-USER> (defun average (list) (/ (+ list) (length list)))

it doesn't work since (+ '(list-of-numbers)) can't be evaluated, hence the arguement of length and the expression in the + are incompatible.

is there a way of cajoling (list) to be evaluated naturally as an expression by + and passed as data to length ? rather than using apply #'

I've tried this:

(defun average (list) (/ (+ list) (length '(list))))

but this doesn't seem to do it either !

Upvotes: 0

Views: 178

Answers (2)

Rainer Joswig
Rainer Joswig

Reputation: 139261

There is no reason not to use APPLY or, better, REDUCE. Here REDUCE is the correct function.

If you program in Lisp, most of the time you have to use a symbol, naming your operation, followed by arguments. This is the general basic expression style in Lisp. You might expect that there are shorter ways to write something like reducing a list with a function. But there isn't, in basic Lisp.

(+ (list 1 2 3 4)) is an error because + expects numbers, not a list.

If you want to sum all numbers in a list, you have to use REDUCE. This operation is also known as folding.

Summing the numbers in a list:

(reduce #'+ (list 1 2 3 4))

Another simple way is to use LOOP:

(loop for n in (list 1 2 3 4) sum n)

Writing something like (length '(list)) makes no sense, since you are quoting a list. A quoted list is treated as data and not code. Since it is constant data, the result is always 1.

One of the Lisp ways to get to short programs is to build up a vocabulary of reusable functions. One doesn't use the shortest notation, but instead one is creating reusable functions with obvious names. Thus, if we sum a lot, we write a sum function:

(defun sum (list)
  (reduce #'+ list))

Then average is:

(defun average (list)
  (/ (sum list) (length list)))

Upvotes: 5

fstamour
fstamour

Reputation: 799

See Common lisp: How many argument can a function take?

It would be preferable to use (reduce #'+ list) instead (apply #'+ list) because apply is subject to the limit of number of argument a that function can take.

So your average function should looks like:

(defun average (list)
  (/ (reduce #'+ list)
     (length list)))

BTW, the code (length '(list)) returns 1 because it returns the lenght of a list containing the symbol "list".

Upvotes: 5

Related Questions