I don't understand what the differences are between (sorry for the contrived example):
(define average
(lambda (elems)
(define length
(lambda (xs)
(if (null? xs)
0
(+ 1 (length (cdr xs))))))
(define sum
(lambda (xs)
(if (null? xs)
0
(+ (car xs) (sum (cdr xs))))))
(define total (sum elems))
(define count (length elems))
(/ total count)))
and
(define average
(lambda (elems)
(letrec ((length
(lambda (xs)
(if (null? xs)
0
(+ 1 (length (cdr xs))))))
(sum
(lambda (xs)
(if (null? xs)
0
(+ (car xs) (sum (cdr xs))))))
(total (sum elems))
(count (length elems)))
(/ total count))))
As far as I can tell, they both create a new scope, and in that scope create 4 local variables that refer to each other and to themselves, and evaluate and return a body.
Am I missing something here, or is letrec
synonymous with scoped define
s?
I know this may be implementation dependent; I'm trying to get an understanding of the fundamentals of Lisps.
Scheme provides two useful variants of let . letrec supports the creation of recursive local procedures, including mutually recursive sets of procedures. let* supports the sequenced binding of variables, where each initial value expression can use the previous bindings.
In a let expression, the initial values are computed before any of the variables become bound; in a let* expression, the bindings and evaluations are performed sequentially; while in a letrec expression, all the bindings are in effect while their initial values are being computed, thus allowing mutually recursive ...
let and let* create new variable bindings and execute a series of forms that use these bindings. let performs the bindings in parallel and let* does them sequentially. first evaluates the expressions init-form-1, init-form-2, and so on, in that order, saving the resulting values.
The phrase "local bindings mode" means that the executable application was created to use a shared memory connection (on the same physical or virtual machine) to connect to the Queue Manager (QM).
You are correct that there are parallels between the define
and letrec
versions of your code. However, the devil is in the details. In R5RS, internal define
has letrec
semantics. In R6RS, internal define
has letrec*
semantics.
What's the difference? Your code has actually just highlighted this difference. As Zhehao's answer mentions, your definition of total
and count
inside the same letrec
as the length
and sum
is incorrect: length
and sum
are not guaranteed to be bound by the time you're evaluating the values of (length elems)
and (sum elems)
, since the binding of those variables is not guaranteed to be left-to-right.
letrec*
is similar to letrec
, but with a left-to-right guarantee. So if you changed your letrec
to letrec*
, it'd be okay.
Now, back to my initial comment: because R5RS's internal define
uses letrec
semantics, even your define
version of the code would be incorrect under an R5RS implementation, but it would be okay under an R6RS implementation, which has letrec*
semantics.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With