Вам нужно знать несколько вещей о Racket:
В Racket каждый файл (начинающийся с #lang
) представляет собой модуль, в отличие от многих (традиционных, r5rs) схем, в которых нет модулей.
Правила области видимости для модулей аналогичны правилам для функции, поэтому в некотором смысле эти определения аналогичны определениям в функции.
Racket оценивает определения слева направо. На схемном жаргоне вы говорите, что определения Racket имеют letrec*
семантику; это отличается от некоторых схем, использующих семантику letrec
, где взаимно рекурсивные определения никогда не работают.
Таким образом, суть в том, что все определения создаются в среде модуля (аналогично функции для локальных определений), а затем инициализируются слева направо. Поэтому обратные ссылки всегда работают, поэтому вы всегда можете сделать что-то вроде
(define a 1)
(define b (add1 a))
Они создаются в одной области действия, поэтому теоретически опережающие определения действительны в том смысле, что они находятся в области действия. Но на самом деле использование значения прямой ссылки не будет работать, поскольку вы получаете специальное значение #<undefined>
до тех пор, пока фактическое значение не будет оценено. Чтобы увидеть это, попробуйте запустить этот код:
#lang racket
(define (foo)
(define a a)
a)
(foo)
Верхний уровень модуля дополнительно ограничен, так что такие ссылки на самом деле являются ошибками, что вы можете увидеть с помощью:
#lang racket
(define a a)
Имея все это в виду, все немного снисходительнее со ссылками внутри функций. Дело в том, что тело функции не выполняется до тех пор, пока функция не будет вызвана, поэтому, если прямая ссылка происходит внутри функции, она действительна (= не получит ошибку или #<undefined>
), если функция все-таки вызвана. привязки были инициализированы. Это относится к простым определениям функций
(define foo (lambda () a))
определения, использующие обычный синтаксический сахар
(define (foo) a)
и даже другие формы, которые в конечном итоге расширяются в функции
(define foo (delay a))
Со всем этим вы не получите никаких ошибок по одному и тому же правилу — когда все использования тел функций происходят после инициализации определений.
Однако одно важное замечание заключается в том, что не следует путать этот вид инициализации с присваиванием. Это означает, что такие вещи, как
(define x (+ x 1))
не эквивалентны x = x+1
в основных языках. Они больше похожи на какие-то var x = x+1
в языке, который завершится ошибкой "ссылка на неинициализированную переменную". Это связано с тем, что define
создает новую привязку в текущей области, а не "модифицирует" существующую.
person
Eli Barzilay
schedule
29.10.2013