I'm in a Scheme class and I was curious about writing a recursive function without using define. The main problem, of course, is that you cannot call a function within itself if it doesn't have a name.
I did find this example: It's a factorial generator using only lambda.
((lambda (x) (x x))
(lambda (fact-gen)
(lambda (n)
(if (zero? n)
1
(* n ((fact-gen fact-gen) (sub1 n)))))))
But I can't even make sense of the first call, (lambda (x) (x x)): What exactly does that do? And where do you input the value you want to get the factorial of?
This is not for the class, this is just out of curiosity.
You define it like this:
which is how
letrec
really works. See LiSP by Christian Queinnec.In the example you're asking about, the self-application combinator is called "U combinator",
The subtlety here is that, because of
let
's scoping rules, the lambda expressions can not refer to the names being defined.When
((U h) 5)
is called, it is reduced to((h h) 5)
application, inside the environment frame created by thelet
form.Now the application of
h
toh
creates new environment frame in whichg
points toh
in the environment above it:The
(lambda (n) ...)
expression here is returned from inside that environment frame in whichg
points toh
above it - as a closure object. I.e. a function of one argument,n
, which also remembers the bindings forg
,h
, andU
.So when this closure is called,
n
gets assigned5
, and theif
form is entered:The
(g g)
application gets reduced into(h h)
application becauseg
points toh
defined in the environment frame above the environment in which the closure object was created. Which is to say, up there, in the toplet
form. But we've already seen the reduction of(h h)
call, which created the closure i.e. the function of one argumentn
, serving as ourfactorial
function, which on the next iteration will be called with4
, then3
etc.Whether it will be a new closure object or same closure object will be reused, depends on a compiler. This can have an impact on performance, but not on semantics of the recursion.