読むポール・グラハムのエッセイプログラミング言語について考えるとLispマクロ唯一の方法です。他のプラットフォームで作業している忙しい開発者として、私は Lisp マクロを使用する機会に恵まれませんでした。話題になっていることを理解したい人として、この機能がなぜそれほど強力なのかを説明してください。
また、これを Python、Java、C#、または C 開発の世界から私が理解できるものと関連付けてください。
ベストアンサー1
簡単に答えると、マクロはCommon Lispやドメイン固有言語(DSL)の言語構文拡張を定義するために使用されます。これらの言語は既存のLispコードに直接埋め込まれます。現在、DSLはLispに似た構文を持つことができます(Peter NorvigのProlog インタプリタCommon Lispの場合)または全く異なるもの(例:挿入記法による数学Clojure の場合)。
より具体的な例を挙げましょう。Pythonには
リスト内包表記が言語に組み込まれています。これにより、よくあるケースに対してシンプルな構文が提供されます。
divisibleByTwo = [x for x in range(10) if x % 2 == 0]
0から9までの偶数をすべて含むリストを生成します。Python 1.5昔はそのような構文はありませんでした。次のような構文を使用していました。
divisibleByTwo = []
for x in range( 10 ):
if x % 2 == 0:
divisibleByTwo.append( x )
これらは両方とも機能的に同等です。信じられないかもしれませんが、Lisp には反復だけを行う非常に限定されたループ マクロがあり、リストの内包表記と同等のことを簡単に行う方法はないと仮定しましょう。
Lisp では、次のように記述できます。この不自然な例は、Python コードと同一であるように選択されたものであり、Lisp コードの良い例ではないことに注意してください。
;; the following two functions just make equivalent of Python's range function
;; you can safely ignore them unless you are running this code
(defun range-helper (x)
(if (= x 0)
(list x)
(cons x (range-helper (- x 1)))))
(defun range (x)
(reverse (range-helper (- x 1))))
;; equivalent to the python example:
;; define a variable
(defvar divisibleByTwo nil)
;; loop from 0 upto and including 9
(loop for x in (range 10)
;; test for divisibility by two
if (= (mod x 2) 0)
;; append to the list
do (setq divisibleByTwo (append divisibleByTwo (list x))))
先に進む前に、マクロとは何かをより詳しく説明する必要があります。マクロとは、コードごとに実行される変換です。つまり、インタープリタ (またはコンパイラ) によって読み取られるコードの一部で、コードを引数として受け取り、操作して結果を返し、その結果がインプレースで実行されます。
もちろん、これは大量の入力であり、プログラマーは怠け者です。そこで、リストの内包表記を行うための DSL を定義することができます。実際、すでに 1 つのマクロ (ループ マクロ) を使用しています。
Lisp では、特別な構文形式がいくつか定義されています。引用符 ( '
) は、次のトークンがリテラルであることを示します。準引用符またはバックティック ( `
) は、次のトークンがエスケープ付きのリテラルであることを示します。エスケープは、コンマ演算子で示されます。リテラルは、'(1 2 3)
Python の に相当します。別の変数に代入することも、その場で使用することもできます。 は、以前に定義された変数であるPython の に相当するものと[1, 2, 3]
考えることができます。このリスト表記は、マクロに組み込まれる魔法の一部です。2 番目の部分は、マクロをコードにインテリジェントに置き換える Lisp リーダーですが、これは次の図でよくわかります。`(1 2 ,x)
[1, 2, x]
x
そこで、(リスト内包の略)というマクロを定義できますlcomp
。その構文は、例で使用したPythonとまったく同じです[x for x in range(10) if x % 2 == 0]
。(lcomp x for x in (range 10) if (= (% x 2) 0))
(defmacro lcomp (expression for var in list conditional conditional-test)
;; create a unique variable name for the result
(let ((result (gensym)))
;; the arguments are really code so we can substitute them
;; store nil in the unique variable name generated above
`(let ((,result nil))
;; var is a variable name
;; list is the list literal we are suppose to iterate over
(loop for ,var in ,list
;; conditional is if or unless
;; conditional-test is (= (mod x 2) 0) in our examples
,conditional ,conditional-test
;; and this is the action from the earlier lisp example
;; result = result + [x] in python
do (setq ,result (append ,result (list ,expression))))
;; return the result
,result)))
これでコマンドラインで実行できます:
CL-USER> (lcomp x for x in (range 10) if (= (mod x 2) 0))
(0 2 4 6 8)
なかなかすばらしいでしょう? これで終わりではありません。メカニズム、あるいは絵筆も使用できます。必要な構文はすべて使用できます。Python や C# のwith
構文、または .NET の LINQ 構文などです。結局のところ、これが Lisp が人々を惹きつける理由です - 究極の柔軟性です。