В настоящее время я работаю над интерпретатором LISP, написанным на Java. Теперь я застрял в закрытии. Я хочу включить такие блокировки:
(define a 1000)
(define closure (lambda (a) (lambda (b) (+ a b))))
(define x (closure 10))
(x 20) --> 30
Итак, (x 20)
должен возвращать 30
. Но, угадайте, что, он возвращает 1020 в моем интерпретаторе. Я думаю, что ошибка в моем классе лямбда. Это выглядит так:
public class LLambda extends LOperation {
private LList parameters;
private LList definitions;
public LLambda(LList parameters, LList definitions) {
this.parameters = parameters;
this.definitions = definitions;
}
public LObject eval(Environment environment, LObject tokens) {
environment = environment.copy();
for(int i = 0; i < parameters.size(); i++) {
LSymbol key = LSymbol.create(parameters.get(i));
LObject object = ((LList) tokens).get(i);
object = object.eval(environment, tokens);
environment.put(key, object);
}
return definitions.eval(environment, tokens);
}
}
Этот класс отлично работает, но он не сохраняет значения среды, чтобы обеспечить закрытие. У кого-то есть идея, как это сделать? И где это сделать? В конструкторе или в методе eval?
И если я не выполняю это:
environment = environment.copy();
Закрытие работает, но оно прерывает некоторые другие тесты.
Спасибо.
(Я также могу загрузить весь источник или дать его бесплатно в GIT).
Этот класс отлично работает, но он не сохраняет значения среды, чтобы обеспечить закрытие. У кого-то есть идея, как это сделать? И где это сделать? В конструкторе или в методе eval?
Да, класс должен сохранять среду. Вообще говоря, переменная-член.:)
Он должен быть в конструкторе, потому что среда привязана во время построения лямбда, а не во время eval.
В eval время исходная среда недоступна: новая среда.
Если ваш диалект лексически ограничен, ваш лямбда не нуждается в параметре среды. Помните, что такое лямбда? Это функция. Для оценки форм требуется среда. Оценка функций не выполняется; оценка функции является вызовом функции и принимает только аргументы. Среды не переходят в функции; функциональные тела оцениваются в инкапсулированном пространстве со своей собственной частной средой. (Наличие функции eval
на лямбда даже кажется неправильным, вы хотите, чтобы это было названо call
или что-то в этом роде. Интерпретируемая лямбда использует службы оценщика, но она не одна.)
Внутри лямбды действие будет состоять в том, чтобы оценить формы тела лямбды. Они будут использовать хранимую среду (не все, что было передано).
Вы должны создать среду, в которой параметры лямбда имеют привязки к значениям аргумента. Это вложено в захваченную среду. (I.e. аргумент под названием x
затеняет захваченную переменную под названием x
).
(У вас уже есть какой-то способ вложенности среды, например, для создания новых привязок, которые относятся к внешней среде.)
Если вы хотите поддерживать динамическое масштабирование в дополнение к лексическому, не должно быть необходимости передавать среду для этого; это может быть сделано неявно. Локальная переменная потока может поддерживать динамическую среду или что-то в этом роде.
(Детали зависят от дизайна интерпретатора, в некоторых проектах всегда присутствует некоторый объект контекста (представляющий интерпретатор). Если вы переходите, скажем, к байт-коду виртуальной машины с явным стеком, вам понадобится.)
Я настоятельно рекомендую прочитать книгу Christian Queinnec Lisp в Small Pieces, в которой подробно описаны многие способы реализации Lisp (или схемы как) оценщики, интерпретаторы, компиляторы.
Вам нужно будет обрабатывать по-разному закрытые значения из локальных значений.