Next: pcaseパターンによる分解, Previous: pcaseの拡張, Up: パターンマッチングによる条件 [Contents][Index]
このサブセクションでは構造化マッチングを容易にするビルトインパターンであるバッククォートスタイルパターン(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にマッチする)ベクターならマッチ。
symbolkeywordnumberstringexpvalの対応する要素が指定されたリテラルオブジェクトとequalならマッチ。
,patternexpvalの対応する要素が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にバインドします。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