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を返します。
オブジェクトに格納された複数の値を抽出することを構造分解と呼びます。pcaseパターンを使用すると、ローカルバインディング(ローカル変数を参照)に似た構造分解バインディングを行うことができます。これにより、互換性のある構造を持つオブジェクトから値を抽出することで、変数の複数の要素に値を代入できます。
本節で説明するマクロでは、pcase
パターンを用いて構造分解バインディングを行います。オブジェクトが互換性のある構造を持つという条件は、そのオブジェクトがパターンに一致しなければならないことを意味します。なぜなら、そうして初めてオブジェクトのサブフィールドを抽出できるからです。例えば:
(pcase-let ((`(add ,x ,y) my-list))
(message "Contains %S and %S" x y))
前述の例と同様の動作をしますが、my-list が適切な要素数を持ち、かつ add を最初の要素とするリストであるかを事前に確認することなく、my-list から直接 x と y を抽出しようとします。
オブジェクトが実際にパターンに一致しなかった場合の正確な挙動は型に依存しますが、本体が黙ってスキップされることはありません。エラーが通知されるか、あるいは一部の変数が
nil
のような任意の値にバインドされた状態で本体が実行されます。例えば、上記のパターンでは、car や
nth といった操作によって x と y が抽出されるため、my-list が短すぎる場合には nil という値が得られます。対照的に、`[add ,x
,y] のようなパターンでは、同じ変数が aref を用いて抽出されるため、my-list が配列でないか、あるいは短すぎる場合にはエラーが通知されます。
バインディングの構造分解に有用な pcase パターンは、一般的に Backquote-Style Patterns で説明されているものであり、これらはマッチするオブジェクトの構造仕様を表現するためです。
束縛の分解を行う別の仕組みについては、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*と同様に、bindingsの次の要素を処理する前に、各expが対応するpatternと照合されます。そのため、各bindingsで導入された変数束縛は、bodyで利用できるだけでなく、それに続くbindingsのexpでも利用可能になります。
pcase-let* と同様ですが、pattern
が対応する exp
と一致しない場合にエラーを通知します。例えば、構造分解したい値が想定通りでない場合に警告を出すことができます。
list の各要素に対して body
を1回実行し、各反復において pattern の変数を list の要素の対応するサブフィールドの値に分割代入します。この代入は pcase-let によって行われるかのように実行されます。pattern が単純な変数である場合、これは結果的に dolist と同等になります(Iteration を参照)。
変数にsetq形式で値を代入し、各valueを対応するpatternに従って分割代入します。
これは lambda
に似ていますが、各引数にパターンを指定できます。例えば、コンスセルを引数に取るシンプルな関数を以下に示します:
(setq fun
(pcase-lambda (`(,key . ,val))
(vector key (* val 10))))
(funcall fun '(foo . 2))
⇒ [foo 20]