Область действия схемы (определить и разрешить)

Итак, я знаю, что в схеме определение предназначено для динамической области видимости и пусть для статической области видимости, но меня смущает следующее:

Если бы у меня был

(let ((x 0))
  (define f (lambda () x))
  (display (f))
  (let ((x 1))
    (display (f))
    )
  )

Он будет отображать 00. Пока все хорошо. Однако, если я добавлю дополнительное определение для x, например:

(let ((x 0))
  (define f (lambda () x))
  (display (f))
  (define x 4)
  (let ((x 1))
    (display (f))
    )
  )

Он будет отображать undefined4. Почему это? Почему определение x после вычисления f влияет на возвращаемое значение (f)? И почему возвращаемое значение «не определено»?

Также стоит упомянуть, что привязка f к letrec вместо define также будет работать:

(let ((x 0))
  (letrec ((f (lambda () x)))
  (display (f))
  (define x 4)
  (let ((x 1))
    (display (f))
    )
  )
)

Возвращает 00.

Примечание. Я использовал DrRacket с установленным языком «Pretty Big».


person user2085086    schedule 18.02.2013    source источник
comment
Что вы подразумеваете под определением для динамической области видимости в Схеме? Кстати, если вы не обязаны использовать его для курса, Pretty Big — это устаревший диалект.   -  person Rhangaun    schedule 19.02.2013
comment
Схема всегда использует статическую область видимости, неверно указывать, что определение предназначено для динамической области видимости, а пусть для статической области видимости.   -  person Óscar López    schedule 19.02.2013
comment
Я согласен с Оскаром: посылка в вопросе либо неверна, либо использует неправильный термин.   -  person dyoo    schedule 19.02.2013
comment
Define разрешает динамическую область видимости. Посмотрите на следующий пример: (определить x 1) (определить f (лямбда() x)) (отобразить (f)) (определить x 2) (отобразить (f)) Отображает 12 В отличие от: (пусть ((x 1 )) (letrec ((f (lambda() x))) (display (f)) (let ((x 2)) (display (f)) ) ) ) который отображает 11   -  person user2085086    schedule 19.02.2013
comment
@ user2085086: нет, вы путаете эффект двух определений верхнего уровня, второе из которых переназначает первое. В некоторых реализациях Scheme переопределение будет рассматриваться как set!. Но это хлопотно и запутанно. Фактически, ваш первый фрагмент кода даже не скомпилируется в стандартном Racket, поскольку компилятор заранее скажет, что повторяющееся определение является незаконным.   -  person dyoo    schedule 19.02.2013


Ответы (3)


Проблема, с которой вы столкнулись во втором случае, заключается в том, что (define x 42) делает x переменной для всей области, в которой она определена. Теперь, хотя переменная определена для всей области видимости, ее значение не определено до фактической (define x 42) строки.

(let ()
  ;; up here, `x` is defined but has an undefined value
  ;; ...
  (define x 42)
  ;; down here `x` has the value 42
  ;; ...
  )

Он действует примерно так:

(let ([x 'undefined])
  ;; ... up here, `x` is 'undefined
  (set! x 42)
  ;; ... down here, `x` is 42
  )
person Greg Hendershott    schedule 19.02.2013
comment
Булочка, а где let ((x 0)) вверху? - person Necto; 25.02.2013

Ваши второй и третий фрагменты кода не являются схемой (ни R5RS, ни R6RS, ни R7RS). <body> (из let и других) определяется как:

<body> -> <definition>* <sequence>
<sequence> -> <command>* <expression>
<command> -> <expression>

и, таким образом, define (то есть <definition>) не может следовать за display (<expression>). Вы, вероятно, получаете запутанные результаты, потому что компилятор/интерпретатор неправильно обрабатывает расширение 'let'.

Вот что делает «хороший» компилятор R6RS:

> (let ((x 0))
  (letrec ((f (lambda () x)))
  (display (f))
  (define x 4)
  (let ((x 1))
    (display (f))
    )
  )
)
Unhandled exception
 Condition components:
   1. &who: define
   2. &message: "a definition was found where an expression was expected"
   3. &syntax:
       form: (define x 4)
       subform: #f
   4. &trace: #<syntax (define x 4)>
> 
person GoZoner    schedule 25.02.2013

Случай 1: тело f привязывается к самому внешнему let в обоих вызовах, в результате чего получается 00, как того требует статическая область.

Случай 2: я не очень уверен в этом, но внутренний (define x 4) затеняет самую внешнюю привязку x=0 и находится в области действия повсюду, даже если текстуально он находится после вызова f. Затем некоторая хитрость оценки заставляет первый вызов произойти до того, как новая привязка x будет полностью инициализирована, поэтому она «неинициализирована». Второй вызов во внутреннем let происходит после того, как все инициализировано, поэтому 4.

Случай 3: Теперь, когда мы явно поместили letrec и define в разные области видимости, f, очевидно, относится к самому внешнему let. Определение здесь ничего не делает.

person hzap    schedule 19.02.2013