Up: Conditionals   [Contents][Index]


10.2.1 パターンマッチングによるcase文

特定の値を、可能なさまざまの場合にたいして比較するには、マクロpcaseが便利です。これは以下のフォームをとります:

(pcase exp branch1 branch2 branch3 …)

branchは、(upattern body-forms…)というフォームです。

これは最初にexpを評価してから、どのbranchを使用するか、その値を各upatternと比較して、その後で対応するbody-forms実行します。一般的なのは、少数の異なる定数値を区別するために使用される場合です:

(pcase (get-return-code x)
  (`success       (message "Done!"))
  (`would-block   (message "Sorry, can't do it now"))
  (`read-only     (message "The shmliblick is read-only"))
  (`access-denied (message "You do not have the needed rights"))
  (code           (message "Unknown return code %S" code)))

最後の条項のcodeは、(get-return-code x)からreturnされた値にバインドされる変数です。

もっと複雑な例として、以下のような小さな式言語のための単純なインタープリターを示します(この例ではレキシカルバインディングが必要なことに注意してください):

(defun evaluate (exp env)
  (pcase exp
    (`(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)     exp)
    ((pred symbolp)     (cdr (assq exp env)))
    (_                  (error "Unknown expression %S" exp))))

`(add ,x ,y)は、expがシンボルaddで始まる3要素のリストかチェックして、その後2番目と3番目の要素を抽出し、それらを変数xyにバインドするパターンです。(pred numberp)expが数字かを単にチェックし、_はすべてのものにマッチする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

pcaseに関係する2種類のパターンがあり、それらはU-patternsQ-patternsと呼ばれます。上述のupatternはU-patternsで、以下の形式をもつことができます:

`qpattern

これは、もっとも一般的なパターンの1つです。このパターンの意図は、バッククォートマクロの模倣です。このパターンは、バッククォート式により構築されるような値にマッチします。わたしたちが行なうのは値の構築ではなくパターンマッチングなので、非クォートは式をどこに挿入するか示すのではなく、かわりにその位置で値にマッチすべき1つのU-patternを指定します。

より具体的には、Q-patternは以下のフォームをもつことができます:

(qpattern1 . qpattern2)

このパターンは、carqpattern1cdrpattern2にマッチする、任意のコンスセルにマッチします。

atom

このパターンは、atomequalな任意のアトムにマッチします。

,upattern

このパターンは、upatternにマッチする任意のオブジェクトにマッチします。

symbol

U-pattern内の単なるシンボルはすべてにマッチし、さらにマッチした値にそのシンボルをバインドするので、body-formsや皇族のパターンから、それを参照することができます。

_

このパターン — いわゆるdon’t careパターン — はシンボルパターンと同様、すべてのものにマッチしますが、シンボルパターンとは異なり、変数へのバインドを行ないません。

(pred pred)

このパターンは、マッチされるオブジェクトで関数predが呼び出したとき、非nilをreturnするものにマッチします。

(or upattern1 upattern2…)

このパターンは、引数のパターンから最初に成立したパターンにマッチします。すべての引数パターンは、同じ変数にバインドされるべきです。

(and upattern1 upattern2…)

このパターンは、すべての引数パターンが成立したときだけマッチします。

(guard exp)

このパターンは調べられるオブジェクトを無視して、expが非nilに評価されたときは成立、それ以外は不成立となります。これは通常、andパターンの内部で使用されます。たとえば、(and x (guard (< x 10)))は10より小さい任意の数字にマッチして、それを変数xにバインドします。