Разница между определением, пусть и заданием! - PullRequest
40 голосов
/ 23 марта 2011

Хорошо, это довольно простой вопрос: я слежу за видео SICP, и меня немного смущают различия между define, let и set!.

1).Сассману в видео define разрешено прикреплять значение к переменной только один раз (кроме случаев, когда в REPL), в частности, два определения в строке не допускаются.Тем не менее, Guile с радостью запускает этот код

(define a 1)
(define a 2)
(write a)

и выдает 2, как и ожидалось.Все немного сложнее, потому что, если я пытаюсь сделать это (РЕДАКТИРОВАТЬ: после вышеуказанных определений)

(define a (1+ a))

Я получаю ошибку, в то время как

(set! a (1+ a))

разрешено.Тем не менее, я не думаю, что это единственная разница между set! и define: что я пропускаю?

2) Разница между define и let озадачивает меня еще больше.Я знаю, что в теории let используется для привязки переменных в локальной области видимости.Тем не менее, мне кажется, что это работает так же с define, например, я могу заменить

(define (f x)
    (let ((a 1))
        (+ a x)))

на

(define (g x)
    (define a 1)
    (+ a x))

и f и g работаютТо же самое: в частности, переменная a также не связана с g.

Единственный способ увидеть это полезное - это то, что let может иметь более короткую область действия, чем целое определение функции.Тем не менее, мне кажется, что всегда можно добавить анонимную функцию, чтобы создать необходимую область, и сразу же вызывать ее, как это делается в javascript.Итак, каково реальное преимущество let?

Ответы [ 4 ]

32 голосов
/ 24 марта 2011

Ваше заблуждение разумно: «let» и «define» оба создают новые привязки.Одним из преимуществ слова «let» является то, что его значение чрезвычайно хорошо определено;между различными системами Scheme (включая Racket) нет абсолютно никаких разногласий по поводу того, что означает простой старый «let».

Форма «define» - это другой котелок с рыбой.В отличие от 'let', он не окружает тело (область, где привязка действительна) круглыми скобками.Кроме того, это может означать разные вещи на верхнем уровне и внутри.Различные системы Схемы имеют совершенно разные значения для «определения».Фактически, Racket недавно изменил значение слова «define», добавив новые контексты, в которых это может происходить.

С другой стороны, людям нравится «define»;у него меньше отступов, и он обычно имеет уровень «делай то, что я имею в виду», позволяющий естественные определения рекурсивных и взаимно рекурсивных процедур.На самом деле, я был укушен этим только на днях:).

Наконец, 'set!';как "пусть", "установить!"довольно просто: он мутирует существующую привязку.

FWIW, один из способов понять эти области в DrRacket (если вы его используете) - это использовать кнопку «Проверить синтаксис», а затем навести курсор на различные идентификаторы, чтобы увидеть, где они связаны.

16 голосов
/ 23 марта 2011

Вы имеете в виду (+ 1 a) вместо (1+ a)? Последнее не является синтаксически допустимым.

Область переменных, определенных let, привязана к последнему, таким образом

(define (f x)
  (let ((a 1))
    (+ a x)))

возможно синтаксически, а

(define (f x)
  (let ((a 1)))
  (+ a x))

нет.

Все переменные должны быть define d в начале функции, поэтому возможен следующий код:

(define (g x)
  (define a 1)
  (+ a x))

пока этот код выдаст ошибку:

(define (g x)
  (define a 1)
  (display (+ a x))
  (define b 2)
  (+ a x))

потому что первое выражение после определения подразумевает, что других определений нет.

set! не определяет переменную, скорее она используется для присвоения переменной нового значения. Поэтому эти определения не имеют смысла:

(define (f x)
  (set! ((a 1))
    (+ a x)))

(define (g x)
  (set! a 1)
  (+ a x))

Допустимое использование для set! следующее:

(define x 12)
> (set! x (add1 x))
> x
13

Хотя это не рекомендуется, поскольку Scheme - функциональный язык.

7 голосов
/ 25 марта 2011

Джон Клементс ответит хорошо. В некоторых случаях вы можете видеть, какими становятся define в каждой версии Схемы, что может помочь вам понять, что происходит.

Например, в Схема Chez 8.0 (которая имеет свои define причуды, особенно по R6RS!):

> (expand '(define (g x)
             (define a 1)
             (+ a x)))
(begin
  (set! g (lambda (x) (letrec* ([a 1]) (#2%+ a x))))
  (#2%void))

Вы видите, что определение "верхнего уровня" становится set! (хотя простое расширение define в некоторых случаях изменит вещи!), Но внутреннее определение (то есть define внутри другого блока) становится letrec*. Различные схемы будут расширять это выражение в разные вещи.

MzScheme v4.2.4 :

> (expand '(define (g x)
             (define a 1)
             (+ a x)))
(define-values
 (g)
 (lambda (x)
   (letrec-values (((a) '1)) (#%app + a x))))
2 голосов
/ 23 марта 2011

Вы можете использовать define более одного раза, но это не так idiomatic: define подразумевает, что вы добавляете определение к окружение и set! подразумевают, что вы изменяете некоторую переменную.

Я не уверен насчет Guile и почему он позволил бы (set! a (+1 a)), но если a еще не определено, это не должно работать. Обычно можно использовать define чтобы ввести новую переменную и изменить ее только с помощью set! позже.

Вы можете использовать приложение анонимной функции вместо let, в факт, что это обычно именно то, что let расширяется, это почти всегда макрос. Это эквивалентно:

(let ((a 1) (b 2))
  (+ a b))

((lambda (a b)
   (+ a b))
 1 2)

Причина, по которой вы будете использовать let, заключается в том, что она понятнее: имена переменных располагаются рядом со значениями.

В случае внутренних определений, я не уверен, что Ясир правильный. По крайней мере, на моей машине, работает Racket в R5RS-режиме и в обычный режим позволяет внутренним определениям появляться в середине определение функции, но я не уверен, что говорит стандарт. В любой случай, гораздо позже в SICP, хитрость, которая определяет внутреннюю позу подробно обсуждается. В главе 4, как реализовать взаимно рекурсивный Внутренние определения изучаются и что это означает для реализации метациркулярного интерпретатора.

Так что держись! SICP - блестящая книга, и видео лекции замечательны.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...