Suppose you want to map the "add three" function over a list. You
don't need to define a name for the function, you can just use a
lambda expression: (lambda (n) (+ n 3))
. This creates
an anonymous "add three" function.
Now suppose you want to map the "factorial" function over a list.
You start with (lambda (x) (if (zerop x) 1 ...
,
but how can you recursively call an anonymous function? We need the
function to have a local name within its own body. One option is to
use the Y operator:
* (map 'list (Y (lambda (fact) (lambda (x) (if (zerop x) 1 (* x (funcall fact (1- x))))))) '(3 5 7)) (6 120 5040)but another popular option is to provide a new special form
* (map 'list (named-lambda fact (x) (if (zerop x) 1 (* x (fact (1- x))))) '(3 5 7)) (6 120 5040)The name
fact
is bound to the lambda
expression only within the body of the lambda expression. You don't
need to defun
a factorial
procedure, you can
just use a named-lambda
.
A little puzzle for you: write named-lambda
. My
answer below.
Just as a let
is syntactic sugar for
a lambda
application, a named-let
is
syntactic sugar for a named-lambda
application. The
name is bound to the lambda expression that performs the variable
binding, so you can use that name to make a recursive call. In
effect, you can re-invoke the named-let
expression with
a fresh set of values.
Scheme hackers will be familiar with named-let
, but it
isn't usual in Common Lisp. It's an easy transformation:
(named-let recur ((x '(a list)) (y 22)) (body)) => (funcall (named-lambda recur (x y) (body)) '(a list) 22)
named-let
is the bee's knees for ad hoc
iteration. The iteration variables are bound to their initial values
by the named-let
, and the body can initiate the next
iteration by tail-calling the named-let
with the updated
values for the iteration variables. Since there is no constraint on
where or when you iterate, or how you update the iteration variables,
this allows very general iteration.
I have seen a tendency for Scheme hackers to overdo it with named
lets. You don't need a named-let
when map
will do. It's usually better to express an iteration through a
higher order function than to write yet another ad hoc
loop in your code. But named-let
is a useful and
powerful tool when the simpler methods aren't cutting it.
Here's what I came up with
for named-lambda
:
(defmacro named-lambda (name (&rest arglist) &body body) `(labels ((,name ,arglist ,@body)) (function ,name)))
Isn't named-lambda very similar to Paul Graham's alambda?
ReplyDeleteSRFI 31 provides the rec macro as follows:
ReplyDelete(define-syntax rec
(syntax-rules ()
((rec (NAME . VARIABLES) . BODY)
(letrec ( (NAME (lambda VARIABLES . BODY)) ) NAME))
((rec NAME EXPRESSION)
(letrec ( (NAME EXPRESSION) ) NAME))))
In s7, define returns its value so (map (define (fact n) (if (< n 2) 1 (* n (fact (- n 1))))) '(3 5)) -> '(6 120)
ReplyDeleteYou could wrap the define in a let as rec does to keep the name out of the calling environment.
At least in Common Lisp, your named-lambda definition doesn't match usage.
ReplyDeleteAs defined, you'd have to say (named-lambda (fact x) ...), but you used (named-lambda fact (x) ...).
The CL defmacro should go (defmacro named-lambda (name (&rest arglist) &body body) ...)
Anonymous pointed out that I posted the wrong version. I updated it.
ReplyDelete