Next: , Previous: , Up: パターンマッチングによる条件   [Contents][Index]


11.4.3 バッククォートスタイルパターン

このサブセクションでは構造化マッチングを容易にするビルトインパターンであるバッククォートスタイルパターン(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が( carqpat1cdrqpat2にマッチする)コンスセルならマッチ。(qpat1 qpat2 …)のように容易に一般化できる。

[qpat1 qpat2qpatm]

expvalが長さmの(0から(m-1)番目の要素がqpat1qpat2、…、qpatmにマッチする)ベクターならマッチ。

symbol
keyword
number
string

expvalの対応する要素が指定されたリテラルオブジェクトとequalならマッチ。

,pattern

expvalの対応する要素がpatternにマッチすればマッチ。patternpcaseがサポートするすべての種類のパターンであることに注意(上記の例ではsecond-elemsymbolコアパターンであり、これはすべてにマッチしてsecond-elemをletでバインドする)。

対応する要素(corresponding element)とはバッククォートスタイルパターンqpatにたいする構造的な位置に等しいようなexpvalの構造的な位置部分のことです(上記の例ではsecond-elemの対応する要素はexpvalの2つ目の要素)。

以下は小さな式言語用の単純なインタープリターの実装用にpcaseを使用する例です(bodyargを正しくキャプチャーするには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つ目の要素を取り出してシンボルxyにバインドします。これは分解(destructuring)と呼ばれています。pcaseパターンによる分解を参照してください。clauseのbodyではxyを評価して結果を加算します。同じように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