S式からラムダを作成する
-
03-07-2019 - |
質問
Common Lispの変数にバインドされたs-expressionがあります:
(defvar x '(+ a 2))
次に、呼び出されたときに、定義されたスコープ内の式を評価する関数を作成します。私はこれを試しました:
(let ((a 4))
(lambda () (eval x)))
and
(let ((a 4))
(eval `(lambda () ,x)))
しかし、これらは両方とも問題を引き起こします。EVALはトップレベルでコードを評価するため、式に含まれる変数をキャプチャできません。 EVALにLETフォームを配置できないことに注意してください。解決策はありますか?
編集:それでは、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を使用して、変数が動的に設定される環境でコンパイルされた関数をFUNCALLすることができます。または、COMPILEを使用して、式を変数を受け入れる関数にコンパイルすることをお勧めします。
Compileは関数定義をリストとして受け入れ、関数に変換します。 SBCLの場合、この関数はマシンコードにコンパイルされ、効率的に実行されます。
最初のオプション(コンパイルとprogvを使用):
(defvar *fn* (compile nil '(lambda () (+ a 2)))
(progv '(a) '(4) (funcall *fn*))
=>
6
2番目のオプション:
(defvar *fn* (compile nil '(lambda (a) (+ a 2))))
(funcall *fn* 4)
=>
6