マクロの1つの楽しい側面は、lispの構文を拡張して新しい構文機能を追加する可能性を与えることです。これは、マクロに渡された引数が実行時にのみ評価され、マクロをコンパイルします。例としてのClojureの最初/最後のスレッドマクロは、-> ->>
そうでない場合は、これらの評価を行った結果が(と言うより何かを受け入れることができなかった、その発現-引数を評価しない(+ 3) => 3
と3
のようなもので、あなたの最初の主要な引数を受け入れることができる機能ではありません(-> 4 (+ 3))
)。
さて、この構文が好きで、Common Lisp実装(それがない)に追加したい場合は、マクロを自分で定義して追加できます。このようなもの:
;;; in SBCL
;;; first thread:
(defmacro -> (x &rest more)
(loop for m in more
for n = `(,(first m) ,x ,@(rest m)) then `(,(first m) ,n ,@(rest m))
finally (return n)))
;;; last thread:
(defmacro ->> (x &rest more)
(loop for m in more
for n = `(,(first m) ,@(rest m) ,x) then `(,(first m) ,@(rest m) ,n)
finally (return n)))
これで、Clojureと同じ方法でCommon Lispでそれらを使用できるようになります。
(-> #'+
(mapcar '(2 3 4) '(1 2 3))) ;; => (3 5 7)
(->> #'>
(sort '(9 8 3 5 7 2 4))) ;; => (9 8 7 5 4 3 2)
またrange
、次のような構文の独自のキーワードを使用して、clojureの関数の新しい構文を作成することもできます。
(from 0 to 10) ;=> (0 1 2 3 4 5 6 7 8 9)
(from 0 to 10 by 0.5) ;;=> (0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0 8.5 9.0 9.5)
このマクロのように定義できます:
(defmacro from
"Just another range!"
[x y z & more]
`(when (= '~y '~'to)
(if '~more (range ~x ~z (second '~more))
(range ~x ~z))))
またはCommon Lispに似たものを追加します(もちろん、これらはすべて、もちろん言語で実行できるためです)。
(defmacro from (x y z &rest r)
`(when (eql ',y 'to)
(if (and ',r (eql (first ',r) 'by))
(loop for i from ,x to ,z by (second ',r) collect i)
(loop for i from ,x to ,z collect i))))
(from 0 to 10) ;=> (0 1 2 3 4 5 6 7 8 9 10)
(from 0 to 5 by 1/2) ;=> (0 1/2 1 3/2 2 5/2 3 7/2 4 9/2 5)