(defun sumlist_1b (numbers) (apply plus numbers))
In this posting, (Part 2), we'll look at improving this implementation by using the apply
function with more than two arguments to enable handling of short lists.
This function, sumlist_1b
, is usually able to add up the numbers in a given list. It is the fastest way I know of from SKILL++ to sum a list, and because of that it is tempting to ignore several cases where it fails.
- the
nil
list - a singleton list
- a list whose length is longer than 65535 in length.
An attempt to sum the elements of the empty list results in the following:
(sumlist_1b nil) *Error* plus: too few arguments (at least 2 expected, 0 given) - nil <<< Stack Trace >>> apply(plus numbers) sumlist_1b(nil)
An attempt to sum the elements of a singleton list results in the following:
(sumlist_1b '(3.4)) *Error* plus: too few arguments (at least 2 expected, 1 given) - (3.4) <<< Stack Trace >>> apply(plus numbers) sumlist_1b('(3.4))
It is somewhat curious but notable that the SKILL plus
function is unable to be called with zero or a single argument.
If you are in charge of the data, for example if your program is generating the lists of numbers which you'd like to sum up, you may already know that that in your application sumlist_1b
will never be called with nil, singleton lists, or extremely long lists. If that is the case there is no need to worry; sumlist_1b
works just fine despite its limitation. However, you need a more robust version of this function, read on.
Try #1 to fix sumlist_1b
The first two of the above limitations can be solved in a straightforward way as special cases in the function implementation.
(defun sumlist_2a (numbers) (cond ((cdr numbers) ; if there is more than one element (apply plus numbers)) ((null numbers) ; if zero elements 0) (t ; a singleton list (car numbers))))
The sumlist_2a
function contains special cases for the nil
list and for a singleton list. The tests in the (cond ...)
are ordered such that the most common case comes first: (cdr numbers)
. If the given list has more than one element, then the cdr
function will return non-nil. I am assuming that this is the most common situation.
Rather than adding additional complexity as in sumlist_2a
, there is a simpler way to extend sumlist_1b
to work on nil and singleton lists.
With two arguments, the apply
function, calls the designated function with the given argument list. A standard (and handy) feature of apply
is that if it is given more than two arguments, the second to penultimate ones have the special meaning that they are implicitly prepended to the final one. For example:
(apply plus 1 2 3 '(4 5 6 7 8))
is equivalent to
(apply plus '(1 2 3 4 5 6 7 8))
This feature of apply
is pretty common for Lisp dialects such as Common Lisp, elisp (emacs lisp), and MIT/GNU Scheme. One would naturally expect the apply
function in SKILL to work the same way, and fortunately it does.
This feature of apply
is of course not very interesting for lists whose contents are explicitly given, because if you can type (apply plus 1 2 3 '(4 5 6 7 8))
you can as easily type (apply plus '(1 2 3 4 5 6 7 8))
. But it does allow us to rewrite the sumlist_1b
function as follows.
(defun sumlist_2b (numbers) (apply plus 0 0 numbers))Why does this work?
This works because prepending 0
twice to the argument list of plus
assures that plus
has at least two arguments. Also, zero
is the arithmetic identity for addition. Arithmetically adding two zeros does not effect the sum -- neither in value nor in type.
Similar problems
Is there a way to construct a function such as sumlist_1b
, sumlist_2a
, or sumlist_2b
which will work for other types of operations like maximization and minimization?
Using the model shown in sumlist_1b
, we can implement a function that will return the maximum element of a given list, provided the list has more than one element.
(defun maxlist_2c (numbers) (apply max numbers))
However, maxlist_2c
fails if the list has one or zero elements. If you want to be able to maximize a list even it it is nil
or a singleton list, you can do something similar to sumlist_2a
. The maximum element of a singleton list is the first (only) element. However, you'd have to define what you mean by the maximum element of an empty list. It does not really make sense in general because the max operation does not have an identity element. While x+0=0+x=x
, there is no number, I, such that max{x,I}=max{I,x}=x
. For this reason the function maxlist_2d
triggers an error for the empty list.
Even though it does not make sense in general, for your particular application it very well might have a meaning; so you can change the call to error by some other code as you like.
(defun maxlist_2d (numbers) (cond ((cdr numbers) (apply max numbers)) ((null numbers) (error "cannot find maximum of the empty list")) (t (car numbers))))
More to come
See Also
- MIT/GNU Scheme -- apply
- Common Lisp -- APPLY
- Emacs Lisp -- apply
Jim Newton