глобальный define
Во-первых, программы верхнего уровня обрабатываются частью реализации, отличной от функции, и определение уже определенной переменной не допускается.
(define a 10)
(define a 20) ; ERROR: duplicate definition for identifier
Может случиться так, что это работает в REPL, так как переопределение является обычным делом, но при запуске программ это категорически запрещено. В R5RS и до этого то, что произошло, недоопределено и не имеет значения, так как спецификация, нарушая ее, больше не является программой Scheme, и разработчики вольны делать все, что хотят. В результате, конечно, многие недоопределенные вещи получают поведение, зависящее от реализации, которое не является переносимым или стабильным.
Решение:
set!
изменяет привязки:
(define a 10)
(set! a 20)
define
в лямбде (функция, пусть, ...)
define
в лямбде — это нечто совершенно другое, обрабатываемое совершенно другими частями реализации. Он обрабатывается макросом/специальной формой lambda
, поэтому он переписывается в letrec*
letrec*
или letrec
используются для того, чтобы сделать функции рекурсивными, поэтому имена должны быть доступны во время вычисления выражений. Из-за этого, когда вы define
n
, он уже затмил n
, который вы передали в качестве аргумента. Кроме того, от разработчиков R6RS требуется сигнализировать об ошибке при оценке привязки, которая еще не инициализирована, и, вероятно, именно это и происходит. До того, как разработчики R6RS могли делать все, что хотели:
(define (func n)
(define n (+ n 1)) ; illegal since n hasn't got a value yet!
n)
На самом деле это становится:
(define func
(lambda (n)
(let ((n 'undefined-blow-up-if-evaluated))
(let ((tmpn (+ n 1)))
(set! n tmpn))
n)))
Теперь компилятор может увидеть, что он нарушает спецификацию во время компиляции, но многие реализации не узнают об этом до запуска.
(func 5) ; ==> 42
Отличный результат в R5RS, если у разработчиков хороший книжный вкус. Разница в версии, которую вы сказали, работает, заключается в том, что это не нарушает правило оценки n
перед телом:
(define (func n)
(define n 5)
n)
становится:
(define func
(lambda (n)
(let ((n 'undefined-blow-up-if-evaluated))
(let ((tmpn 5)) ; n is never evaluated here!
(set! n tmpn))
n)))
Решения
Используйте неконфликтующее имя
(define (func n)
(define new-n (+ n 1))
new-n)
Используйте let
. У него нет собственной привязки, когда выражение оценивается:
(define (func n)
(let ((n (+ n 1)))
n))
person
Sylwester
schedule
06.09.2017