Subject: Re: Functions in Lisp
From: rpw3@rpw3.org (Rob Warnock)
Date: Wed, 22 Feb 2006 04:55:40 -0600
Newsgroups: comp.lang.lisp
Message-ID: <4POdneRIme2x3mHeRVn-pQ@speakeasy.net>
Espen Vestre  <espen@vestre.net> wrote:
+---------------
| peder@news.klingenberg.no (Peder O. Klingenberg) writes:
| > That said, there's probably a reason why I couldn't immediately come
| > up with an example of a function (as opposed to AND) from my own code
| > that relies on the evaluation order of arguments.
| 
| Defaults to keyword arguments are obvious candidates, and I thought I
| used defaults computed from the obligatory parameters all the time,
| but I actually only found one example in recent code.
+---------------

Oddly enough, I ran across a case of wanting to do exactly that just
last night, when writing a generalization for MAKE-LIST that allows
circular tails [yes, while playing around with the circular lists
mentioned in the "Subject: equal lists" thread] and is also capable
of initialing the elements of the list to a function of their position
[pardon the ugly draft code!]:

    (defun make-list* (size &key initial-element
				 (circular-tail-size 0)
				 (initial-element-function
				  (constantly initial-element)))
      (loop with prefix-length = (- size circular-tail-size)
	     and list = (make-list size)
	     and merge = nil
	    for i below size
	     and node on list
	     and last = nil then node
	do (setf (car node) (funcall initial-element-function i))
	when (= i prefix-length)
	  do (setf merge node)
	finally (when (plusp circular-tail-size)
		  (setf (cdr last) merge))
		(return list)))

The idea being that if you stick to MAKE-LIST args you get MAKE-LIST
semantics, but if not, well:

    cmu> (make-list* 5)

    (NIL NIL NIL NIL NIL)
    cmu> (make-list* 5 :initial-element 'foo)

    (FOO FOO FOO FOO FOO)
    cmu> (make-list* 5 :initial-element-function #'identity)

    (0 1 2 3 4)
    cmu> (make-list* 5 :initial-element-function (lambda (x) (expt 2 x)))

    (1 2 4 8 16)
    cmu> (make-list* 5 :initial-element-function (lambda (x) (expt 2 x))
			 :circular-tail-size 3)

    (1 2 . #1=(4 8 16 . #1#))
    cmu> 

Anyway, somewhere along the way I realized that if the
INITIAL-ELEMENT-FUNCTION argument were made to be the primary
source of initialization instead of INITIAL-ELEMENT [which,
as you'll see above is now used *only* by the default for
INITIAL-ELEMENT-FUNCTION], then by putting the keyword args in
the right order with the right defaults then "the right thing"
would simply happen automagically.  ;-}

[But, no, off hand I can't think of any other code I've written
depending on the keyword arg order quite so strongly...]


-Rob

p.s. Can anyone think of better (shorter, mainly!) names for the
above args than INITIAL-ELEMENT-FUNCTION & CIRCULAR-TAIL-SIZE?
I tried INITIALIZATION-FUNCTION, but that didn't scan as well.

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607