Up: Conditionals   [Contents][Index]


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

condフォームにより、あらかじめ記述された既知の特定の値と式の値を比較する述語条件を使用して選択肢を選択できます。しかし広範な値クラス間を区別する、より一般的な条件にもとづいて選択肢を選択するのが有用なこともあります。pcaseマクロにより、一連のパターンにたいする式の値のマッチングにもとづいて選択肢を選択できます。パターンにはリテラル値(condで使用した比較用のリテラル値)や、予想される式の値のより一般的な構造記述を使用できます。

Macro: pcase expression &rest clauses

expressionを評価して、任意個数の選択肢の中からexpressionの値にもとづいた選択を行う。可能な選択肢はclausesで指定され、それぞれが(pattern body-forms…)という形式のリストでなければならない。pcaseexpressionの値と各clauseのpatternのマッチを試みる。値がマッチしたらそのclauseが成功となり、pcaseはそれのbody-formsを評価して、body-formsの最後の値をリターンする。残りのclausesはすべて無視される。

patternパートはバッククォートでクォートされたQPatternと、クォートされていないUPatternのいずれかで指定できる。UPatternsのほうが単純なのでそれから説明する。

注意: 以下のパターンの記述ではpcaseの1つ目の引数となるexpressionの値を参照するために、“マッチされる値”という言葉を使用している。

UPatternには以下の形式を指定できる:

'val

マッチされる値がvalequalならマッチ。

atom

任意のatom (キーワード、数字、文字列)にマッチする(これらは自己クォートされるのでこの種のUPatternは実際には'atomの略記である)。文字列(浮動小数点数)は同じ内容(値)の任意の文字列(浮動小数点数)とマッチすることに注意。

_

任意の値にマッチする。これはdon’t carewildcardとして知られる。

symbol

任意の値にマッチする。さらにマッチした値をsymbolにletバインドするので、body-formsや後続のパターンからそれを参照することができる。

(pred predfun)

マッチされる値を引数として述語関数predfunを呼び出して、非nilをリターンしたらマッチ。predfunは後述するフォームのいずれかを指定できる。

(guard boolean-expression)

boolean-expressionが非nilに評価されたらマッチ。これにより以前のUPatternで、値(マッチされる値を含む)にバインドされたシンボルを参照するブール条件をUPatternに含めることができる。典型的にはUPattern and内で使用される(以下参照)。たとえば(and x (guard (< x 10)))は10より小さい任意の数にマッチして、その数を変数xにletバインドする。

(let upattern expression)

指定されたexpressionが指定されたupatternにマッチしたらマッチ。これによりpcaseの1つ目の引数だけでなく、任意の式の値にパターンをマッチできる(upatternはUPattern symbolを使用してシンボルを値にバインドできるのでletと呼ばれる。たとえば((or `(key . ,val) (let val 5)) val))。

(app function upattern)

マッチされる値にfunctionを適用してupatternにマッチする値がリターンされたらマッチ。これはUPattern predと似ているが、これはブールの真値ではなくUPatternにたいして結果をテストする点が異なる。function呼び出しは後述のフォームのいずれかを使用できる。

(or upattern1 upattern2…)

引数のUPatternのいずれかがマッチしたらマッチ。マッチする最初のUPatternが見つかったら残りはテストされない。この理由により、マッチされる値にシンボルをletバインドするすべてのUPatternは同じシンボルをバインドすること。

(and upattern1 upattern2…)

引数のUPatternすべてがマッチしたらマッチ。

predappのUPatternで使用される関数呼び出しは、以下のいずれかのフォームをもつことができる:

integerpのような関数シンボル

この場合には、その名前つき関数がマッチされる値に適用される。

ラムダ関数 (lambda (arg) body)

この場合には、そのラムダ関数がマッチされる値を単一の引数として呼び出される。

(func args…)

これは指定されたn個の引数で呼び出される関数である。関数はこれらn個の引数と、マッチされる値であるn+1番目の引数を追加して呼び出される。

以下はUPatternを使用した説明用の例です:

(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)))

加えてより強力なバッククォートされたパターンを使用できます。これらを使用すればpcaseの1つ目の引数の式の値を、その構造(structure)の仕様とマッチさせることができます。たとえば1つ目の要素が特定の文字列で、2つ目の要素が`("first" ,second-elem)のようなバッククォートされた任意の値であるような2要素のリストを、値として強制指定することができます。

バッククォートされたパターンは`qpatternという形式をもち、qpatternは以下の形式をもつことができます:

(qpattern1 . qpattern2)

マッチされる値が、carqpattern1cdrqpattern2にマッチするようなコンスセルならマッチ。これは(qpattern1 qpattern2 …)のように、容易にバッククォートされたリストに一般化できる。

[qpattern1 qpattern2qpatternm]

マッチされる値が、長さm0から(m-1)番目の要素がそれぞれqpattern1qpattern2、…、qpatternmにマッチするようなベクターならマッチ。

atom

マッチされる値の対応する要素が指定されたatomequalならマッチ。

,upattern

マッチされる値の対応する要素が指定されたupatternとマッチすればマッチ。

QPatternは後述のpcase-defmacroを使用してUPatternのトップレベルで実装されているので、QPatternの使用はUPatternを使用することでのみ表現可能なことに注意。とはいえQPatternの使用により、多くの場合コードの可読性は向上するだろう。

以下はpcaseを使用して、小さな式言語用のシンプルなインタープリターを実装する例です(この例にはレキシカルバインディングが必要なことに注意。)Lexical Bindingを参照のこと):

(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にバインドするパターンです。それからxyを評価して結果を加算します。同様にcallfnのパターンは、関数呼び出しに相当するものを2つ実装します。(pred numberp)expが数であるかをチェックして、もしそうならそれを評価します。(pred symbolp)はシンボルにマッチして、その連想をリターンします。最後に_はすべてにマッチする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-defmacroを使用することにより追加のUPatternを定義できます。

Macro: pcase-defmacro name args &rest body

pcaseにたいして新たな種類のUPatternを定義する。新たなUPatternは(name actual-args)のように呼び出されるだろう。bodyには、UPattern nameを他の何らかのUPatternに書き換える方法を記述すること。argsactual-argsにバインドされる環境でbodyを評価した結果がこの書き換えとなる。


Up: Conditionals   [Contents][Index]