マクロを書いて、次のように実行してみました。しかし、実行に失敗しました。
(defmacro times_two (var) (* 2 var))
(times_two '(+ 1 2))
私の想像では、展開は(* 2(+ 1 2))になると思います。実行後、結果は6になります。しかし失敗しました。
どうしてか分かりません。Emacs lispのマニュアルを読みましたが、それでも理解できません。拡張を構築する際の正確な手順は、いったい何であるかを知りたいです。通訳は何をしましたか?
これらのフォームを Emacs で評価すると、2 番目のフォームを評価するときに次のエラー メッセージが表示されます。
Debugger entered--Lisp error: (wrong-type-argument number-or-marker-p (quote (+ 1 2)))
*(2 (quote (+ 1 2)))
(lambda (var) (* 2 var))((quote (+ 1 2)))
(times_two (quote (+ 1 2)))
eval((times_two (quote (+ 1 2))))
eval-last-sexp-1(nil)
eval-last-sexp(nil)
call-interactively(eval-last-sexp nil nil)
これは、マクロがどのように展開されたかを示しており、何が問題だったかがわかります。(最終展開は一番上です。)
引用符で囲まれた式'(+ 1 2)
は times_two マクロに渡されますが、引用符で囲まれたリストは*
関数の有効な引数ではありません。
ここで実際に必要なのは次のとおりです。
(defmacro times_two (var) `(* 2 ,var))
(times_two (+ 1 2))
一般に、マクロの結果は最終的な値ではなく、新しい Lisp コードになることに注意してください。マクロを作成する目的は、必要な結果が得られるフォームを作成することです。したがって、ほとんどの場合、マクロは最終的に準引用符 (`) 構文を使用することになります。
コンパイル時と実行時を混同していると思われます。マクロはコンパイル時に実行され、実行時に実行されるコードを生成します。一般的に言えば、これらをまっすぐに保つのは難しく、マクロを書くのが難しくなります。
いずれにせよ、これを ielm に入れると、次のようになります。
ELISP> (defmacro times_two (var)
(* 2 var))
times_two
ELISP> (times_two '(+ 1 2))
*** Eval error *** Wrong type argument: number-or-marker-p, (quote (+ 1 2))
ELISP>
問題の少なくとも一部は '(+ 1 2) ですが、引用符を削除すると、次のようになります。
ELISP> (times_two (+ 1 2))
*** Eval error *** Wrong type argument: number-or-marker-p, (+ 1 2)
ELISP>
elisp は '(+ 1 2) と (+ 1 2) を置いている場所で数字またはマーカーを探しているようです。数字を使ってみましょう:
ELISP> (times_two 3)
6
それはうまくいきます。
興味深いことに、これをマクロ展開すると次のようになります。
ELISP> (macroexpand '(times_two 3))
6
これはおそらく私たちが望んでいるものではありません。
マクロを書くときは、実行時に評価される式を返す必要があります。したがって、数値を返す代わりに、次のように試すことができます。
ELISP> (defmacro times_two (var)
`(* 2 ,var))
バッククォート (quasiquote) はリストを作成する方法ですが、コンマを使用して補間することもできます。このように times_two を定義すると、次のようになります。
ELISP> (times_two (+ 1 2))
6
そして、次の拡張:
ELISP> (macroexpand '(times_two (+ 1 2)))
(* 2
(+ 1 2))
それはまさにあなたが想像した方法です。