「言語のコード認識」に関しては、Lisp とそのマクロ機能、特に Common Lisp に勝るものはありません。しかし、ほとんどの場合、コンパイル時またはマクロ展開時にオブジェクトの型が不明であるというトレードがあります。リテラルの場合、型は既知であるため、オブジェクトがリテラルであるかどうかをテストし、そうである場合は型に基づいて 1 つの方法で処理し、検出された変数を準備する積極的なマクロの例を見つけることができます。実行時の型検査用。
これは、私が数年前にCLLIBライブラリ (CLOCC ライブラリの一部)から採用した例です。目標は、一致するプレフィックスを持つ他の文字列からプレフィックス文字列を切り取る関数を提供することです。プレフィックスは、マクロ展開時に認識される場合と、認識されない場合があります。そうであれば、最適化できます。最初にプレフィックスの長さを計算し、それをリテラルとして埋め込むことで、生成された関数の呼び出しごとに再計算されないようにします。マクロは最初は気が遠くなりますが、実際に生成されるコードは小さいものです。
(defmacro after-prefix-core (comparison-op prefix string &optional length)
"Similar to cllib:string-beg-with-cs."
(flet ((chop (prefix prefix-length string string-length)
`(when (and (>= ,string-length ,prefix-length)
(,comparison-op ,prefix ,string :end2 ,prefix-length))
(subseq ,string ,prefix-length ,string-length))))
(let* ((gstring (gensym "STRING-"))
(gstring-length (gensym "STRING-LENGTH-")))
`(let* ((,gstring ,string)
(,gstring-length ,(or length `(length ,gstring))))
,(if (stringp prefix)
;; Constant -- length known at expansion time.
(let ((prefix-length (length prefix)))
(chop prefix prefix-length gstring gstring-length))
;; Other form -- length not known at expansion time.
(let ((gprefix (gensym "PREFIX-"))
(gprefix-length (gensym "PREFIX-LENGTH-")))
`(let* ((,gprefix ,prefix)
(,gprefix-length (length ,gprefix)))
,(chop gprefix gprefix-length gstring gstring-length))))))))
(defmacro after-prefix (prefix string &optional length)
"Similar to cllib:string-beg-with."
`(after-prefix-core string-equal ,prefix ,string ,length))
(defmacro after-prefix-cs (prefix string &optional length)
"Similar to cllib:string-beg-with-cs."
`(after-prefix-core string= ,prefix ,string ,length))
フォームを見る
(if (stringp prefix)
途中で?これは、マクロ展開時に最初の引数を調べており、引数がリテラルかシンボルかによって、その型がわかっている場合とわかっていない場合があります。型がシンボルの場合、実行時まで待って、それを他の値を指す変数として再検討する必要があると想定します。
フォームの展開は次の(after-prefix foo bar)
とおりです。
(LET* ((#:STRING-5340 BAR) (#:STRING-LENGTH-5341 (LENGTH #:STRING-5340)))
(LET* ((#:PREFIX-5342 FOO) (#:PREFIX-LENGTH-5343 (LENGTH #:PREFIX-5342)))
(WHEN
(AND (>= #:STRING-LENGTH-5341 #:PREFIX-LENGTH-5343)
(STRING-EQUAL #:PREFIX-5342 #:STRING-5340 :END2 #:PREFIX-LENGTH-5343))
(SUBSEQ #:STRING-5340 #:PREFIX-LENGTH-5343 #:STRING-LENGTH-5341))))
#:PREFIX-LENGTH-5343
変数は の計算された長さにFOO
バインドされ、ここでは variable にバインドされていることに注意してください#:PREFIX-5342
。
(after-prefix "foo" bar)
ここで、プレフィックスが文字列リテラルになっているフォームの展開を見てください。
(LET* ((#:STRING-5463 BAR) (#:STRING-LENGTH-5464 (LENGTH #:STRING-5463)))
(WHEN (AND (>= #:STRING-LENGTH-5464 3) (STRING-EQUAL "foo" #:STRING-5463 :END2 3))
(SUBSEQ #:STRING-5463 3 #:STRING-LENGTH-5464)))
現在、「foo」の長さを計算する必要はありません。3としてインライン化されています。
この例では大変な作業のように見えるかもしれませんが、あなたの質問が意見を述べているように、そのようなことができることは良い力です.