Confused by the difference between let and let* in

2019-01-06 18:53发布

问题:

Can anyone explain the difference simply? I don't think I understand the concept from the textbooks/sites I have consulted.

回答1:

If you use let, you can't reference bindings previously defined in the same let expression. For example, this won't work:

(let ((x 10)
      (y (+ x 6))) ; error! unbound identifier in module in: x
  y)

But if you use let*, it's possible to refer to previous bindings in the same let* expression:

(let* ((x 10)
       (y (+ x 6))) ; works fine
  y)
=> 16

It's all here in the documentation.



回答2:

Let is parallel, (kind of; see below) let* is sequential. Let translates as

((lambda(a b c)  ... body ...)
  a-value
  b-value
  c-value)

but let* as

((lambda(a)
    ((lambda(b)
       ((lambda(c) ... body ...)
        c-value))
     b-value))
  a-value)

and is thus creating nested scope blocks where b-value expression can refer to a, and c-value expression can refer to both b and a. a-value belongs to the outer scope. This is also equivalent to

(let ((a a-value))
  (let ((b b-value))
    (let ((c c-value))
      ... body ... )))

There is also letrec, allowing for recursive bindings, where all variables and expressions belong to one shared scope and can refer to each other (with some caveats pertaining to initialization). It is equivalent either to

(let ((a *undefined*) (b *undefined*) (c *undefined*))
  (set! a a-value)
  (set! b b-value)
  (set! c c-value)
  ... body ... )

(in Racket, also available as letrec* in Scheme, since R6RS), or to

(let ((a *undefined*) (b *undefined*) (c *undefined*))
  (let ((_x_ a-value) (_y_ b-value) (_z_ c-value))   ; unique identifiers
    (set! a _x_)
    (set! b _y_)
    (set! c _z_)
    ... body ... ))

(in Scheme).

update: let does not actually evaluate its value-expressions in parallel, it's just that they are all evaluated in the same initial environment where the let form appears. This is also clear from the lambda-based translation: first the value expressions are evaluated each in the same, outer environment, and the resulting values are collected, and only then new locations are created for each id and the values are put each in its location. We can still see the sequentiality if one of value-expressions mutates a storage (i.e. data, like a list or a struct) accessed by a subsequent one.



标签: scheme let