(let loop ([n 1] [sum 0]) (if (> n 100) sum (loop (add1 n) (+ n sum))))
The bindings for n and sum are initial values, which change each time around the loop. The name loop is arbitrary, but it's commonly used.
In truth, a named let creates a procedure. In DrScheme, you can verify this by entering:
(let loop () loop)
The procedure is recursive, since we can call it anywhere within the body of the named let. It's possible, and sometimes useful, to use the procedure created by a named let in a non-tail position. Therefore, using a named let does not guarantee loop-like behavior.
Using a named let can obviate the need for a an explicit helper function. Let's rewrite fact2 from above, which used a helper, with a named let:
; version 3 ; nonnegative-number -> positive-number (define (fact3 n) (let loop ([new-n n] [accum 1]) (if (zero? new-n) accum (loop (sub1 new-n) (* new-n accum)))))
The variable new-n is initially bound to n, and accum is initially bound to 1. The recursive call to loop is in tail position, so fact3 exhibits loop-like behavior. This Scheme code works very much like fact_c, written in C/C++.
Many other recursive procedures that we've seen can be built with named lets:
(define (map f lst) (let fun ([lst lst]) (if (null? lst) null (cons (f (car lst)) (fun (cdr lst))))))
Note that this version of map, like the version we saw earlier, is not tail-recursive. Also, the let introduces a new binding for lst that shadows the original binding. That's idiomatic, if a bit confusing. It makes some sense because the inner binding variable is initially bound to the same value as the outer binding variable.
Here's a tail-recursive version:
(define (map f lst) (let loop ([lst lst] [result null]) (if (null? lst) (reverse result) (loop (cdr lst) (cons (f (car lst)) result)))))
The tail-recursion benefit comes at the cost of a list reversal.
Scheme has reverse as a primitive, though it's easy enough to write ourselves using a named let:
(define (reverse lst) (let loop ([lst lst] [result null]) (if (null? lst) result (loop (cdr lst) (cons (car lst) result)))))
Observe that this code is similar to the code for the version of map we just saw. That's because the loop in map was building up a list in reverse order, which we had to ``undo'' with a call to reverse.