Создание лямбды из s-выражения
-
03-07-2019 - |
Вопрос
У меня есть s-выражение, привязанное к переменной в Common Lisp:
(defvar x '(+ a 2))
Теперь я хочу создать функцию, которая при вызове оценивает выражение в области, в которой оно было определено.Я пробовал это:
(let ((a 4))
(lambda () (eval x)))
и
(let ((a 4))
(eval `(lambda () ,x)))
Но оба из них создают проблему:EVAL будет оценивать код на верхнем уровне, поэтому я не могу захватить переменные, содержащиеся в выражении.Обратите внимание, что я не могу поместить форму LET в EVAL.Есть ли какое-нибудь решение?
РЕДАКТИРОВАТЬ:Итак, если нет решения проблемы EVAL, как еще это можно сделать?
РЕДАКТИРОВАТЬ:Возник вопрос о том, что именно я пытаюсь сделать.Я пишу компилятор.Я хочу принять s-выражение с переменными, закрытыми в лексической среде, где это выражение определено.Возможно, действительно лучше написать это как макрос.
Решение
Вам необходимо создать код, имеющий необходимые привязки.Оберните LET вокруг вашего кода и привяжите каждую переменную, которую вы хотите сделать доступной в своем коде:
(defvar *x* '(+ a 2))
(let ((a 4))
(eval `(let ((a ,a))
,*x*)))
Другие советы
CLISP реализует расширение для оценки формы в лексической среде.Учитывая тот факт, что это расширение, я подозреваю, что вы не сможете сделать это в соответствии со стандартами.
(ext:eval-env x (ext:the-environment))
Какова реальная проблема, которую вы хотите решить?Скорее всего, вы пытаетесь решить эту проблему неправильно.Лексические привязки предназначены для вещей, которые лексически появляются в их области видимости, а не для случайных вещей, которые вы получаете извне.
Может быть, вам нужно динамическое закрытие?Такой вещи не существует в Common Lisp, хотя она есть в некоторых диалектах Lisp (например, Pico Lisp, насколько я понимаю).
Обратите внимание, что вы может сделайте следующее, аналогично:
(defvar *a*)
(defvar *x* '(+ *a* 2)) ;'
(let ((a 10))
;; ...
(let ((*a* a))
(eval *x*)))
Однако я советую вам хорошенько подумать, действительно ли вы этого хотите.
В Common Lisp вы можете определить *evalhook* Что позволяет вам передать среду в (eval ...)
. *evalhook*
не зависит от платформы.
Можно использовать COMPILE для компиляции выражения в функцию, а затем использовать PROGV для ФУНКЦИОНАЛЬНОГО ВЫЗОВА скомпилированной функции в среде, где переменные устанавливаются динамически.Или, что еще лучше, используйте COMPILE для компиляции выражения в функцию, принимающую переменные.
Компиляция принимает определение функции в виде списка и превращает его в функцию.В случае SBCL эта функция компилируется в машинный код и выполняется эффективно.
Первый вариант (с использованием компиляции и progv):
(defvar *fn* (compile nil '(lambda () (+ a 2)))
(progv '(a) '(4) (funcall *fn*))
=>
6
Второй вариант:
(defvar *fn* (compile nil '(lambda (a) (+ a 2))))
(funcall *fn* 4)
=>
6