11

非常に強力な Lisp マクロを完全に理解しようとする中で、ある疑問が頭に浮かびました。マクロに関する黄金律は、 「関数が機能するときは決してマクロを使用しない」というものであることを私は知っています。ただし、Chapter 9 - Practical: Building a Unit Test Framework - 本から Practical Common Lisp を読んで、以下のマクロを紹介しました。このマクロの目的は、テスト ケース式の重複を取り除くことであり、それに伴う結果の誤ったラベル付けのリスクがあります。

;; Function defintion. 

(defun report-result (result form)
  (format t "~:[FAIL~;pass~] ... ~a~%" result form))

;; Macro Definition

(defmacro check (form)
  `(report-result ,form ',form))

OK、その目的は理解できましたが、マクロの代わりに関数を使用することもできました。たとえば、次のようになります。

(setf unevaluated.form '(= 2 (+ 2 3)))

(defun my-func (unevaluated.form)
  (report-result (eval unevaluated.form) unevaluated.form))
  1. これは、指定されたマクロが単純すぎるためにのみ可能ですか?
  2. さらに、Lisp マクロ システムは、コード自体 (制御構造、関数など) が LIST として表されているため、反対者に比べて非常に強力ですか?
4

3 に答える 3

14

しかし、それがマクロであれば、次のことができます。

(check (= 2 (+ 2 3)))

関数を使用すると、次のことを行う必要があります。

(check '(= 2 (+ 2 3)))

また、マクロで(= 2 (+ 2 3))はコンパイラによって実際にコンパイルされますが、関数では eval 関数によって評価されますが、必ずしも同じではありません。

補遺:

はい、関数を評価しているだけです。それが何を意味するかは、実装に依存します。解釈できる人もいれば、コンパイルして実行できる人もいます。しかし、単純なことは、システムごとにわからないということです。

他の人が言及している null レキシカル環境も重要です。

検討:

(defun add3f (form)
  (eval `(+ 3 ,form)))

(demacro add3m (form)
  `(+ 3 ,form))

次に観察します。

[28]> (add3m (+ 2 3))
8
[29]> (add3f '(+ 2 3))
8
[30]> (let ((x 2)) (add3m (+ x 3)))
8
[31]> (let ((x 2)) (add3f '(+ x 3)))

*** - EVAL: variable X has no value
The following restarts are available:
USE-VALUE      :R1      Input a value to be used instead of X.
STORE-VALUE    :R2      Input a new value for X.
ABORT          :R3      Abort main loop
Break 1 [32]> :a

これは、ほとんどのユースケースで非常に厄介です。xeval には字句環境がないため、囲んでいる から を「見る」ことはできませんlet

于 2012-07-21T20:28:58.700 に答える
4

より良い置換はeval、すべてのケースで期待どおりに実行されず (たとえば、レキシカル環境にアクセスできない) ではなく、やり過ぎです (こちらを参照してください: https://stackoverflow.com/ a/2571549/977052 )、しかし、次のような無名関数を使用するもの:

(defun check (fn)
  (report-result (funcall fn) (function-body fn)))

CL-USER> (check (lambda () (= 2 (+ 2 3))))

ちなみにRubyではこうなっている(無名関数が呼ばれprocsている)。

しかし、ご覧のとおり、(シンタックス シュガーを追加しない限り) いくらかエレガントではなくなり、実際にはもっと大きな問題があります: function-bodyLisp には関数がありません (非標準的な方法で実現することはできますが)。ご覧のとおり、全体として、この特定のタスクの場合、代替ソリューションは大幅に劣りますが、場合によってはそのようなアプローチが機能する可能性があります。

ただし、一般に、マクロに渡された式のソース コードで何かを行いたい場合 (通常、これがマクロを使用する主な理由です)、関数は十分ではありません。

于 2012-07-21T20:28:28.853 に答える
3

このreport-result関数には、ソースコードと実行結果の両方が必要です。

マクロCHECKは、単一のソースフォームから両方を提供します。

たくさんのcheckフォームをファイルに入れると、Lispファイルをコンパイルする通常のプロセスを使用して簡単にコンパイルできます。チェックコードのコンパイル済みバージョンを取得します。

関数を使用してEVAL(より適切に使用するとCOMPILE)、ソース評価を後で延期することになります。また、それが解釈されているのかコンパイルされているのかも明確ではありません。コンパイルの場合、後でコンパイラのチェックを取得します。

于 2012-07-22T02:25:31.830 に答える