2013-02-21 1 views
23

Может ли кто-нибудь объяснить разницу просто? Я не думаю, что понимаю концепцию из учебников/сайтов, с которыми я консультировался.Смущает разница между let и let * в схеме

+2

Возможный дубликат [Scheme Confusing Let and Let \ *] (http: // stackoverflow.com/questions/8036840/schem-confusing-of-let-and-let) –

+4

@DavidPfeffer - Кажется, это не обман. Этот вопрос задает очень специфическое взаимодействие вложенных 'let' и 'let *', в то время как этот запрашивает общий обзор. – Inaimathi

+0

просто путают человеческое объяснение машинного выражения: o – Nishant

ответ

23

Если вы используете let, вы не можете ссылаться на привязки, ранее определенные в том же выражении let. Например, это не будет работать:

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

Но если вы используете let*, то можно сослаться на предыдущие привязки в том же let* выражение:

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

Это все here в документации.

+1

Я не вижу его в документации (где ваши точки ссылки, текущая версия 5.3.6), поэтому я тоже был сбит с толку. В документации для 'let' говорится, что« Первая форма оценивает «val-exprs» слева направо, ... », поэтому неясно, что они оцениваются параллельно. – Alexey

+0

@Alexey не оценивает их параллельно. Как сказано в документах: * «Первая форма оценивает« val-exprs »слева направо, создает новое местоположение для каждого« id »и помещает значения в места« * - значение, сначала они оцениваются и итоговые значения собираются, и только ***, тогда для каждого * 'id' * создаются *** новые местоположения, и значения помещаются каждый в своем местоположении. Вы все еще можете видеть последовательность, если один из * 'val-exprs' * мутирует хранилище (т. Е. Данные, такие как список или структура), к которому обращается последующий. –

26

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 ...) 

(in 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 ...)) 

(in Scheme).

обновление: let фактически не оценивает свои значения-выражения параллельно, это просто, что все они оцениваются в той же начальной среде, где появляется форма let. Это также видно из перевода на основанное lambda: сначала значение выражения вычисляются каждый в той же среде, внешней, и полученные значения собраны, и только затем новые места создаются для каждого идентификатора и значения ставятся каждый в своем местоположении. Мы все еще можем видеть последовательность, если одно из значений-выражений мутирует хранилище (т. Е. Данные, такие как список или структура), к которым обращается последующий.