0

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 !

trev66
  • 1
  • 1
    Is there a practical reason you want to be able to do this? I think your problem is the notion that the syntax (function (arg1 arg2)) is somehow more "natural" than (function arg1 arg2). – Paul Richter Oct 02 '14 at 01:33
  • 1
    no reason, I'm just trying to understand ! Again no, (function arg1 arg2) seems the more "natural" (highly subjective ! admittedly), it just seems to me that it would be more "natural" to operate on how the args are seen by the function rather than "over riding" the function (with sharp-quote) to fit the args. If you see what I mean. Since quote(list) bypasses the default evaluation rule I was just wondering if there was some "inverse" of the quote that I'd missed somewhere. – trev66 Oct 02 '14 at 11:28

2 Answers2

5

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".

Community
  • 1
  • 1
fstamour
  • 760
  • 4
  • 9
5

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)))
Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346