Почему вы можете переопределить "лямбда"?
Я не понимаю следующее поведение между этими двумя программами Scheme:
Программа 1:
(define a
(begin
(display "hmmm")
(newline)
lambda))
Эта программа, запустить с помощью scheme test.ss
, дает мне синтаксическую ошибку на lambda
строка без распечатывания строки "hmm"
,
Программа 2:
(define lambda 5)
(define a (+ 1 2 lambda))
Конечным результатом здесь является то, что a
равно 8
,
Поведение в первой программе - это поведение, которое я ожидаю в обеих программах. Что меня смущает, так это то, что вторая программа не выходит из строя с синтаксической ошибкой. Ясно, я переопределяю lambda
, но я думаю, что это не с синтаксической ошибкой, прежде чем этот код может быть фактически запущен. Мне кажется, что для того, чтобы знать, что это не синтаксическая ошибка, вам необходимо запустить программу, но если бы это было поведение, то я бы ожидал, что первая программа отобразит строку до того, как произойдет ошибка.
Короче говоря, почему первая программа приводит к синтаксической ошибке, а вторая - нет?
2 ответа
В схеме lambda
а также define
привязки верхнего уровня на этапе компиляции. define
занимает ровно два операнда, и поскольку вы указали 4, он должен сначала среагировать на это. Итак, давайте сначала исправим это:
(define a
(begin
(display "hmmm")
(newline)
lambda)))
Теперь вы получаете сообщение об ошибке lambda
, Это примитивная форма для создания процедур, поэтому компилятор считает, что вы используете ее неправильно:
(lambda (x) (+ x x)) ; implementation of double
Если бы вы определили lambda
в качестве переменной ошибка не произойдет, так как, хотя это способ создания процедур, вы можете создавать переменные с одинаковыми именами.
(define lambda 10)
(define a
(begin (display "hmmm")
(newline)
lambda))
; ==> 10 (and it printed "hmmm")
Компилятор знает лексическую природу кода. Он точно знает, какие привязки определены, а какие на уровне и в какой фазе. Верхний уровень lambda
больше недоступно.
Теперь в вашей второй программе вы определяете lambda
а затем использовать его, как я мой последний пример, который также работает.
Обратите внимание, что в R5RS компилятор предполагает, что переопределения библиотеки и примитивных процедур совместимы и могут постоянно сворачиваться:
(define (+ a b)
(string-append a b))
(display (+ 4 5)) ; displays 9
В его защиту я нарушил отчет R5RS, сделав мой +
несовместимыми. Если бы это не был верхний уровень, все было бы хорошо:
(let ((+ (lambda (a b) (string-append a b))))
(+ 4 5)) ; no loinger top level, will signal n error!
Как упомянула Алексис, совершенно нормально переопределить лямбду.
В первом примере вы вызываете процедуру define
на аргументы a
, (display "hmmm")
, (newline)
а также lambda
, Поскольку Scheme - нетерпеливый язык, он пытается оценить каждый аргумент перед выполнением, что, скорее всего, приводит к сбою при вычислении лямбды (потому что в этом случае лямбда - это процедура, ожидающая некоторый идентификатор аргумента: https://docs.racket-lang.org/guide/lambda.html).
Второй пример успешен, потому что при вызове define
на аргументы a
и суммирование (+ 1 2 lambda)
суммирование разрешается в материальный тип (потому что лямбда была переопределена как целое число).
Надеюсь, это поможет.