質問
私はずっと取り組んできました 実用的な Common Lisp そして、演習として、数値が別の数値の倍数であるかどうかを判断するマクロを作成することにしました。
(defmacro multp (value factor)
`(= (rem ,value ,factor) 0))
となることによって :(multp 40 10)
true と評価されますが、(multp 40 13)
ではない
問題は、このマクロが機能するかどうかです リーク 何らかの方法で?また、これは「良い」Lispですか?使用できる既存の関数/マクロはすでにありますか?
解決
Siebel は、考えられるリークの原因について (とにかく単純なケースについて) 広範な概要を提供していますが、ここにはそれらの情報はありません。両方 value
そして factor
順番に一度だけ評価され、 rem
副作用はありません。
ただし、この場合マクロを使用する理由がないので、これは良い Lisp ではありません。機能
(defun multp (value factor)
(zerop (rem value factor)))
すべての実用的な目的において同一です。(使用に注意してください zerop
. 。この場合は物事がより明確になると思いますが、テストしている値がゼロ以外の値であっても意味がある可能性があることを強調する必要がある場合には、 (= ... 0)
良いかもしれない)
他のヒント
あなたのマクロは私には問題ないようです。リーキーマクロが何であるかはわかりませんが、あなたのマクロは非常に単純で、gensymを必要としません。これが「優れた」Lisp であるかどうかに関する限り、私の経験則では、関数が機能しない場合にのみマクロを使用することです。この場合、マクロの代わりに関数を使用できます。ただし、このソリューションがうまく機能するのであれば、それを使用しない理由はありません。
原則として、ユーザーは次のことを行うことができます。
(flet ((= (&rest args) nil))
(multp 40 10))
これは NIL と評価されます...ただし、ANSI CL では、CL:= を含むほとんどの標準シンボルの再バインドが違法とされているため、この特定のケースでは安全側にいます。
もちろん、一般的には、参照の不透明性 (マクロが展開されるコンテキストから識別子を取得する) とマクロの不衛生性 (展開されたコードに識別子が漏洩する) の両方に注意する必要があります。
いいえ、マクロの「字句クロージャ」で導入されたシンボルは外部に解放されません。
たとえ偶発的な漏れがほとんどの場合に起こるとしても、漏れは必ずしも悪いことではないことに注意してください。私が取り組んだあるプロジェクトでは、次のようなマクロが役立つことがわかりました。
(defmacro ana-and (&rest forms)
(loop for form in (reverse forms)
for completion = form then `(let ((it ,form))
(when it
,completion))
finally (return completion)))
これにより、シーケンス内の以前の呼び出しから引き継がれた引数 (および NIL を返すことによって失敗が通知される) を使用して、シーケンス内で実行する必要がある処理を「ショートサーキット」することができました。このコードの具体的なコンテキストは、構成ファイル用の手書きのパーサー用です。構成ファイルの構文は十分に組み合わされており、パーサー ジェネレーターを使用して適切なパーサーを作成するのは、手動で作成するよりも手間がかかります。