私はレキシカルバインディングを使用するように自分のコードを変換しようとしています。
ユーザがカスタマイズ可能なリスト( template-replace)からラムダ関数(
)です。 replacer-inner関数は引数を取らずに文字列を返しますが、変数 replacer-inner
へのlet-bound)を呼び出す関数( format-template
マッチ文字列に基づいた関数の関数(-functions foo
bar
および/または baz
は list
の引数の一部として format-template
に渡されます。
(setq lexical-binding t)
(defcustom template-replace-functions
'(("email"
(lambda () user-mail-address)
("apples"
(lambda ()
(cond ((= foo 1)
"one")
((= foo 2)
"two")
((= foo 3)
"three")
(t "four"))))
("bananas"
(lambda ()
(if (eq bar 'move)
"movement" "sit still")))))
"Association list of replacement functions.
For each STRING, the corresponding FUNCTION is called with no
arguments and must return a string."
:type '(repeat (group string function))
:group 'spice-girls)
(defun format-template (list)
(let* ((foo (nth 0 list))
(bar (nth 1 list))
(baz (nth 2 list))
template
(replacer-outer
(lambda ()
(replace-regexp-in-string
"\${\([^\s\t\n]*?\)}"
(lambda (match)
(let* ((key (match-string 1 match))
(replacer-inner
(cadr (assoc key template-replace-functions))))
(if (and replacer-inner
(stringp (funcall replacer-inner)))
(funcall replacer-inner) "")))
template t t))))
(setq template
...)
(if template (funcall replacer-outer))))
たとえば、 list
には bar
と baz
にレットバインドされた3つの要素が含まれています。 match-stringのキー
は、 template-replace-functions
の関数に関連する "bananas"
内部には:
(lambda ()
(if (eq bar 'move)
"movement" "sit still"))
この時点で、上記のラムダ関数は、動的バインディングを使っても問題ないが、字句バインディングを使ってうまくいくわけではない bar
を知る必要があります。
私の質問は、lambda関数が foo
bar
のlet-bound値から取る replacer-inner
baz
?
( replace-inner
を含む replace-outer
関数は、実際の関数の2つの場所のいずれかから呼び出されるため、何かを混乱させるだけです。私はここでインラインで書くことができたかもしれませんが、問題に追加する場合は、この方法でインクルードしました。)
編集:
途中で...
(setq lexical-binding t
foo 0
bar 0)
(setq inline-fun-1
'(lambda ()
(setq return
(if (eq foo 1)
"Pass" "Fail"))))
(defmacro lex-fun ()
`(let* ((foo 1)
(bar 1)
return)
(funcall ,inline-fun-1)))
(lex-fun) ; -> "Pass"
(defun inline-fun-2 ()
(setq return
(if (eq bar 1)
"Pass" "Fail")))
(defmacro lex-fun ()
`(let* ((foo 1)
(bar 1)
return)
(funcall ,inline-fun-2)))
(lex-fun) ; -> Lisp error: (void-variable inline-fun-2)
したがって、マクロ内のラムダ関数を拡張すると動作するように見えますが、名前付き関数ではありません。しかし、私はユーザーのラムダまたは名前付きの関数を許可したいです。これを回避するには?
短い答え:できません。これが、この種のバインディングが「字句」と呼ばれる理由です。変数が定義域から逃げるのを防ぎます。
この問題に対処する方法の1つは、バナナがバナナの値を抽出する方法を知っている「バナナ」関数にデータ構造を渡すことができたということです。私。
(defcustom template-replace-functions
'(("bananas" . (lambda (env)
(let ((x (gethash "x" env "")))
(do-replacements-with x))))
...)))
これは、 env
がハッシュテーブルであることを前提としています。
さて、 eq
ではなく eql
を使用する方が良いでしょう。私はEmacs Lispがどのようなタイプのデータでも eq
の振る舞いをどのように保証しているとは思わない(例えば、同じ名前のインターンシンボルも< code> eq )。