このサブセクションでは構造化マッチングを容易にするビルトインパターンであるバッククォートスタイルパターン(backquote-style patterns)について説明します。背景についてはパターンマッチングによる条件を参照してください。
バッククォートスタイルパターンは(pcase-defmacro
を使用して作成された)強力なpcase
パターン拡張であり、その構造(structure)の仕様にたいするexpvalのマッチを容易にします。
たとえば1つ目の要素が特定の文字列、2つ目の要素が任意の値であるような2要素リストのexpvalにたいするマッチはコアパターンを使用して記述できます:
(and (pred listp) ls
(guard (= 2 (length ls))) (guard (string= "first" (car ls))) (let second-elem (cadr ls)))
しかし等価なバッククォートスタイルパターンで記述することもできます:
`("first" ,second-elem)
バッククォートスタイルパターンはより簡潔かつexpvalの構造と似ており、ls
のバインドを要しません。
バッククォートスタイルパターンは`qpat
のような形式をもちます。ここでqpatは以下の形式をもつことができます:
(qpat1 . qpat2)
expvalが(
car
がqpat1、cdr
がqpat2にマッチする)コンスセルならマッチ。(qpat1 qpat2 …)
のように容易に一般化できる。
[qpat1 qpat2 … qpatm]
expvalが長さmの(0
から(m-1)
番目の要素がqpat1、qpat2、…、qpatmにマッチする)ベクターならマッチ。
symbol
keyword
number
string
expvalの対応する要素が指定されたリテラルオブジェクトとequal
ならマッチ。
,pattern
expvalの対応する要素がpatternにマッチすればマッチ。patternはpcase
がサポートするすべての種類のパターンであることに注意(上記の例ではsecond-elem
はsymbolコアパターンであり、これはすべてにマッチしてsecond-elem
をletでバインドする)。
対応する要素(corresponding
element)とはバッククォートスタイルパターンqpatにたいする構造的な位置に等しいようなexpvalの構造的な位置部分のことです(上記の例ではsecond-elem
の対応する要素はexpvalの2つ目の要素)。
以下は小さな式言語用の単純なインタープリターの実装用にpcase
を使用する例です(body
とarg
を正しくキャプチャーするにはfn
のclause内でlambda式にレキシカルバインディングが必要なことに注意):
(defun evaluate (form env) (pcase form (`(add ,x ,y) (+ (evaluate x env) (evaluate y env)))
(`(call ,fun ,arg) (funcall (evaluate fun env) (evaluate arg env))) (`(fn ,arg ,body) (lambda (val) (evaluate body (cons (cons arg val) env))))
((pred numberp) form) ((pred symbolp) (cdr (assq form env))) (_ (error "Syntax error: %S" form))))
最初の3つのclauseではバッククォートスタイルパターンが使用されています。`(add ,x
,y)
はform
がリテラルシンボルadd
から始まる3要素リストであることをチェックしてから2つ目と3つ目の要素を取り出してシンボルx
とy
にバインドします。これは分解(destructuring)と呼ばれています。pcase
パターンによる分解を参照してください。clauseのbodyではx
とy
を評価して結果を加算します。同じようにcall
clauseは関数呼び出しを実装して、fn
clauseは無名関数定義を実装します。
残りのclauseではコアパターンが使用されています。(pred
numberp)
はform
が数値ならマッチします。マッチした場合にはbodyがそれを評価します。(pred
symbolp)
はform
がシンボルならマッチします。マッチした場合にはbodyはenv
内のシンボルを照合して、それの連想値をリターンします。最後の_
はすべてにマッチするcatch-allパターンなので、構文エラーの報告に適しています。
以下は評価した結果を含む、この小さな言語のサンプルプログラムの例です:
(evaluate '(add 1 2) nil) ⇒ 3 (evaluate '(add x y) '((x . 1) (y . 2))) ⇒ 3 (evaluate '(call (fn x (add 1 x)) 2) nil) ⇒ 3 (evaluate '(sub 1 2) nil) ⇒ error