[plt-scheme] a few questions (was: Scheme questions?)
> Okay, as a newcomer to the language, letrec seems reasonably intuitive
> to me, named let is mysterious. (I think of letrec as binding a set of
> symbols to lambda expressions, where the expressions may involve mutual
> recursion. It seems to me that in
>
> (let whatever ((..)...)
>
> whatever is used in the body of the expression as if though it were
> itself a lambda expression, but how this works (and what the correct
> syntax is) seems rather mysterious.
Hi Gregory,
I tend to use named-let as a way of writing loops in Scheme. For example,
if we were to try to find an element in a list, we might write something
like this:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (my-member elt lst)
(let loop ((lst lst))
(cond ((null? lst) (list))
((equal? elt (car lst)) lst)
(else (loop (cdr lst))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
And although this can be rewritten by using just letrec:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (my-member2 elt lst)
(letrec
((loop
(lambda (lst)
(cond ((null? lst) (list))
((equal? elt (car lst)) lst)
(else (loop (cdr lst)))))))
(loop lst)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
it feels slightly more verbose than the named-let version. So as far as
power is concerned, named-let doesn't give us anything new, but the
named-let syntactic sugar makes it a little more convenience to define
these one-shot looping functions on the fly.
The example above shows that we can look at named-lets as really a letrec
in disguise. We can say this more formally, to tell Scheme how to do this
syntax translation from a named-let to a real letrec, by defining a
syntax:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-syntax my-named-let
(syntax-rules ()
((_ function-name ((id val) ...)
e1
e2 ...)
(letrec
((function-name
(lambda (id ...)
e1
e2 ...)))
(function-name val ...)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
This syntax definition tries to show how we might take something that uses
a named-let, and describe it in terms of a letrec. And we can use it!
;;;;;;
> (my-named-let pairing-loop ((names '(danny andy jerry)))
(if (<= (length names) 1)
(list)
(cons (list (car names) (cadr names))
(pairing-loop (cdr names)))))
((danny andy) (andy jerry))
;;;;;;
If we're curious, we can watch this expansion in action by explicitely
calling PLT Scheme's syntax expander to take our expression above and
expand it once:
;;;;;;
> (syntax-object->datum
(expand-once
'(my-named-let pairing-loop ((names '(danny andy jerry)))
(if (<= (length names) 1)
(list)
(cons (list (car names) (cadr names))
(pairing-loop (cdr names)))))))
(letrec ((pairing-loop (lambda (names) (if (<= (length names) 1) (list)
(cons (list (car names) (cadr names)) (pairing-loop (cdr names)))))))
(pairing-loop (quote (danny andy jerry))))
;;;;;;
Hmmm, the result does look like it works, but it's a slight mess to read.
Let's pretty-up that output to make it better indented, using the
pretty-printing library in PLT Scheme's 'mzlib' standard library:
(http://download.plt-scheme.org/doc/299.400/html/mzlib/mzlib-Z-H-34.html)
;;;;;;
> (require (lib "pretty.ss"))
>
> (pretty-display
(syntax-object->datum
(expand-once
'(my-named-let loop ((names '(danny andy jerry)))
(if (<= (length names) 1)
(list)
(cons (list (car names) (cadr names))
(loop (cdr names))))))))
(letrec ((loop
(lambda (names)
(if (<= (length names) 1)
(list)
(cons (list (car names) (cadr names)) (loop (cdr names)))))))
(loop '(danny andy jerry)))
;;;;;;
I hope this helps!