pcaseパターンによる分解 ¶pcaseのパターンはあるオブジェクトがマッチ可能なフォーム上の条件を表現するだけではなく、それらのオブジェクトのサブフィールドの抽出もできます。たとえば以下のコードにより、変数my-listの値であるリストから2つの要素を抽出できます:
(pcase my-list
(`(add ,x ,y) (message "Contains %S and %S" x y)))
これはxとyを抽出するだけではなく、加えてmy-listが正確に3つの要素を含むリストであり、最初の要素がシンボルaddかどうかをテストします。これらのテストのいずれかが失敗したら、pcaseはmessageを呼び出さずに即座にnilをリターンします。
あるオブジェクトから格納された複数の値を抽出する処理は分割(destructuring)という処理としても知られています。pcaseパターンの使用によりバインディングの分割(destructuring
binding)を処理することが可能になります。これはローカルバインディング(ローカル変数を参照)と似ていますが、互換性のあるオブジェクトから値を抽出することにより、変数の複数要素に値を与えることができます。
このセクションで説明したマクロはバインディングを分割するためにpcaseパターンを使用しています。オブジェクト構造に互換性があるという条件は、そのオブジェクトがパターンにマッチしなければならないことを意味しています。なぜならマッチした場合のみオブジェクトのサブフィールドが抽出可能になるからです。たとえば:
(pcase-let ((`(add ,x ,y) my-list))
(message "Contains %S and %S" x y))
これは最初にmy-listが正しい個数の要素をもつリストであり、かつ1つ目の要素がaddか検証せずに、my-listから直接xとyの抽出を行う点を除いて前の例と同じことを行います。
実際にオブジェクトがパターンにマッチしない場合のには、たとえbodyが暗黙にスキップされることはないとしても、その振る舞いは未定義でありエラーがシグナルされるか、あるいはいくつかの変数がnilのような任意の値にバインドされた状況でbodyが実行されるかもしれず、その挙動はオブジェクトのタイプ次第です。たとえば上記のパターンではcarやnthのような操作によってxとyが得られるので、my-listが短すぎる場合には値としてnilが得られるでしょう。それとは対照的に`[add
,x
,y]のようなパターンでは同じ変数を抽出するためにarefを用いられますがmy-listが配列以外、あるいは短すぎる場合にはエラーがシグナルされるでしょう。
バインディングの分割に有用なpcaseパターンとしては、マッチされるオブジェクト構造の仕様を表現するバッククォートスタイルパターンで説明したパターンが一般的です。
バインディングの分割にたいする代替え機能についてはseq-letを参照してください。
bindingsに応じて変数のバインディング分割を行い、それからbodyを評価する。
bindingsは(pattern exp)という形式のバインディングのリスト。ここでexpは評価する式、patternはpcaseパターン。
expはすべて最初に評価されて、その後で対応するpatternにマッチされて、bodyの内部で使用可能な変数バインディングが導入される。この変数バインディングはpatternの要素を、評価されたexpの対応する要素の値に分割してのバインディングすることにより生成される。
以下はその例:
(pcase-let ((`(,major ,minor)
(split-string "image/png" "/")))
minor)
⇒ "png"
bindingsに応じて変数のバインディング分割を行い、それからbodyを評価する。
bindingsは(pattern
exp)という形式のバインディングのリスト。ここでexpは評価する式、patternはpcaseパターン。この変数バインディングはpatternの要素を、評価されたexpの対応する要素の値に分割してバインディングすることにより生成される。
pcase-letとは異なり(しかしlet*と同じように)、各expはbindingsの次要素の処理前に対応するpatternにたいしてマッチされるので、各bindingsのいずれかによって導入される変数バインディングはbody内で利用可能になるのに加えて、その後に続くbindingsのexp内で利用可能になる。
繰り返しごとにpatternの変数をlistの要素の対応するサブフィールドに分割バインディングしながら、listの各要素ごとに一度bodyを実行する。このバインディングはpcase-letの場合のように行われる。patternが単なる変数ならdolistと等価(繰り返しを参照)。
patternに応じて各valueの分割を行い、setqフォーム内の変数に値を割り当てる。
lambdaと同様だが、各引数はパターンでも良い。たとえば以下は引数としてコンスセルを受け取る単純な関数の例:
(setq fun
(pcase-lambda (`(,key . ,val))
(vector key (* val 10))))
(funcall fun '(foo . 2))
⇒ [foo 20]