Up: Conditionals [Contents][Index]
condフォームにより、あらかじめ記述された既知の特定の値と式の値を比較する述語条件を使用して選択肢を選択できます。しかし広範な値クラス間を区別する、より一般的な条件にもとづいて選択肢を選択するのが有用なこともあります。pcaseマクロにより、一連のパターンにたいする式の値のマッチングにもとづいて選択肢を選択できます。パターンにはリテラル値(condで使用した比較用のリテラル値)や、予想される式の値のより一般的な構造記述を使用できます。
expressionを評価して、任意個数の選択肢の中からexpressionの値にもとづいた選択を行う。可能な選択肢はclausesで指定され、それぞれが(pattern
body-forms…)という形式のリストでなければならない。pcaseはexpressionの値と各clauseのpatternのマッチを試みる。値がマッチしたらそのclauseが成功となり、pcaseはそれのbody-formsを評価して、body-formsの最後の値をリターンする。残りのclausesはすべて無視される。
patternパートはバッククォートでクォートされたQPatternと、クォートされていないUPatternのいずれかで指定できる。UPatternsのほうが単純なのでそれから説明する。
注意:
以下のパターンの記述ではpcaseの1つ目の引数となるexpressionの値を参照するために、“マッチされる値”という言葉を使用している。
UPatternには以下の形式を指定できる:
'valマッチされる値がvalとequalならマッチ。
atom任意のatom
(キーワード、数字、文字列)にマッチする(これらは自己クォートされるのでこの種のUPatternは実際には'atomの略記である)。文字列(浮動小数点数)は同じ内容(値)の任意の文字列(浮動小数点数)とマッチすることに注意。
_任意の値にマッチする。これはdon’t careやwildcardとして知られる。
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すべてがマッチしたらマッチ。
predとappの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)マッチされる値が、carがqpattern1、cdrがqpattern2にマッチするようなコンスセルならマッチ。これは(qpattern1 qpattern2 …)のように、容易にバッククォートされたリストに一般化できる。
[qpattern1 qpattern2 … qpatternm]マッチされる値が、長さmで0から(m-1)番目の要素がそれぞれqpattern1、qpattern2、…、qpatternmにマッチするようなベクターならマッチ。
atomマッチされる値の対応する要素が指定されたatomとequalならマッチ。
,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つ目の要素を抽出して変数xとyにバインドするパターンです。それからxとyを評価して結果を加算します。同様にcallとfnのパターンは、関数呼び出しに相当するものを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を定義できます。
pcaseにたいして新たな種類のUPatternを定義する。新たなUPatternは(name
actual-args)のように呼び出されるだろう。bodyには、UPattern
nameを他の何らかのUPatternに書き換える方法を記述すること。argsがactual-argsにバインドされる環境でbodyを評価した結果がこの書き換えとなる。
Up: Conditionals [Contents][Index]