Может ли кто-нибудь объяснить разницу просто? Я не думаю, что понимаю концепцию из учебников/сайтов, с которыми я консультировался.
Смущает разница между 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 и значения ставятся каждый в своем местоположении. Мы все еще можем видеть последовательность, если одно из значений-выражений мутирует хранилище (т.е. Данные, такие как список или структура), к которым обращается последующий.