Может ли кто-нибудь объяснить разницу просто? Я не думаю, что понимаю концепцию из учебников/сайтов, с которыми я консультировался.
Смущает разница между let и let * в Scheme
Ответ 1
Если вы используете let
, вы не можете ссылаться на другие привязки, которые появляются в том же выражении let
.
Например, это не сработает:
(let ((x 10)
(y (+ x 6))) ; error! unbound identifier: x
y)
Но если вы используете let*
, можно сослаться на предыдущие привязки, которые появляются в том же выражении let*
:
(let* ((x 10)
(y (+ x 6))) ; works fine
y)
=> 16
Все это здесь, в документации.
Ответ 2
Let
является параллельным, (вид, см. ниже) let*
является последовательным. Let
означает
((lambda(a b c) ... body ...)
a-value
b-value
c-value)
но let*
как
((lambda(a)
((lambda(b)
((lambda(c) ... body ...)
c-value))
b-value))
a-value)
и, таким образом, создает блоки вложенных областей, где выражение b-value
может ссылаться на a
, а выражение c-value
может ссылаться как на b
, так и на a
. a-value
относится к внешней области. Это также эквивалентно
(let ((a a-value))
(let ((b b-value))
(let ((c c-value))
... body ... )))
Существует также letrec
, что позволяет использовать рекурсивные привязки, где все переменные и выражения принадлежат одной общей области и могут ссылаться друг на друга (с некоторыми оговорками, относящимися к инициализации). Это эквивалентно либо
(let ((a *undefined*) (b *undefined*) (c *undefined*))
(set! a a-value)
(set! b b-value)
(set! c c-value)
... body ... )
(в Racket, также доступный как letrec*
на схеме, так как R6RS) или
(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 ... ))
(в схеме).
update: Let
фактически не оценивает свои значения-выражения параллельно, просто они все оцениваются в той же начальной среде, где появляется форма Let
. Это также ясно из перевода lambda
: сначала выражения значения оцениваются каждый в одной и той же внешней среде, и полученные значения собираются, и только , а затем создаются новые местоположения для каждого id и значения ставятся каждый в своем местоположении. Мы все еще можем видеть последовательность, если одно из значений-выражений мутирует хранилище (т.е. Данные, такие как список или структура), к которым обращается последующий.