パターンマッチの基本
rubyのパターンマッチの大きな特徴は下記の3つ。
- 配列やハッシュの構造をパターン化して条件分岐させる
in
節で=
を使わずにローカル変数の宣言と代入が行われる- 独自の変数スコープ(有効範囲)を作らない
上の1.については、パターンマッチは下記のような構文になっている。
case 式 in パターン1 パターン1にマッチした時の処理 in パターン2 パターン2にマッチした時の処理 in パターン3 パターン3にマッチした時の処理 end
見た目はcase
文っぽいけど、in
の部分が違う。条件分岐という特徴からも、パターンマッチもcase式の一種ということだと思う。
ちなみに本の中では、case/when
式で書かれたものをcase文と呼び、case/in
式で書かれたものをパターンマッチと呼ぶ。
上の2.については、下記のコードを見るとイメージがつく。
# 配列の場合 case [1, 2, 3] in [a, b, c] puts "#{a} #{b} #{c}" end #=> 1 2 3 # ハッシュで値を代入するローカル変数を明示的に指定する場合 case {name: 'Prius', engine: '98ps', motor: '72ps'} in {name: vehicle_name, engine: vehicle_engine, motor: vehicle_motor} puts "#{vehicle_name} #{vehicle_engine} #{vehicle_motor}" end #=> Prius 98ps 72ps # ハッシュで値を省略する場合 case {name: 'Prius', engine: '98ps', motor: '72ps'} in {name: , engine: , motor: } puts "#{name} #{engine} #{motor}" end #=> Prius 98ps 72ps
パターンマッチの利用パターン
パターンマッチを利用するパターンは下記の7パターン。
valueパターン
value
パターンの特徴は、in
節に数値や文字列を直接指定できるという点。case
節の式とin
節の値が等しければ実行される。
country = 'italy' case country in 'japan' 'こんにちは' in 'italy' then 'Ciao' # thenを使えば一行で書ける end #=> "Ciao" message = # case文と同様に戻り値を返すので変数への代入もできる。 case country in 'japan' 'こんにちは' in 'italy' 'Ciao' end message #=> "Ciao"
variableパターン
variable
パターンの特徴は、in
節のパターンに変数を書いて、ローカル変数の宣言と代入を同時に行うという点。『パターンマッチの基本』の2.の内容にあたる。事前に定義した変数などをin
節で使用できる。その際は、^
を使う。
alise, name = 'Alise', 'Alise' case name in ^alise # in 'Alise'と書いたのと同じ 'Hey, Alise' end
arrayパターン
array
パターンの特徴は、in
節に[]を使って配列の構造パターンを指定するという点。『パターンマッチの基本』の2.の配列の内容にあたる。入れ子関係になっていてもOK。
case [1, [2, 3]] in [a, b] "a=#{a}, b=#{b}" end #=> "a=1, b=[2, 3]" case [1, 2, 3] in [a, *rest] # *を使って、任意の長さを指定できる。 "a=#{a}, rest=#{rest}" end #=> "a=1, b=[2, 3]"
hashパターン
hash
パターンの特徴は、in
節に{}
を使ってハッシュの構造パターンを指定するという点。値に変数を指定すると、その変数に対応する値が格納される。キーの順番はパターンマッチに影響しない。
hash
パターンは、ハッシュの各要素がin
節で指定したパターンに部分位置していればマッチしたとみなす。よってin
の順番に気をつけないと、永久に実施されないメソッドができてしまう。ただ、in
節に{}
を書いた場合は、『空のハッシュに完全一致』がマッチの条件になる。
基本的には、『パターンマッチの基本』の2.のhashの内容にあたる。
**
をつけることで、残りカスをまとめて変数に入れられる。
case{name: 'Alise', age: 20, gender: :female} in {name: 'Alise', **rest} "rest=#{rest}" end #=> "rest={:age=>20, :gender=>:female}"
asパターン
as
パターンの特徴は、パターンマッチでマッチしたオブジェクトを変数に代入するという点。=> 変数名
と書いてあげると、マッチしたオブジェクトを代入できる。
case {name: 'Alise', age: 20, gender: :female} in {name: String => name, age: 18.. => age} "name=#{name}, age=#{age}" end #=> "name=Alise, age=20"
alternativeパターン
alternative
パターンの特徴は、2つ以上のパターンを指定し、どれか1つにマッチすればマッチしたとみなす点。パターンを|
で連結する。
case 2 in 0 | 1 | 2 'matched' end #=> "matched" # _を使うことで、オブジェクトの構造をマッチさせられる。変数_への参照はしない。 case [2021, 4, 1] in [_,_] | [_, _, _]# 配列の要素が2または、3つならマッチ 'matched' end #=> "matched"
findパターン
find
パターンの特徴は、ある範囲を指定して、マッチするものを探すという点。
case[13, 11, 9, 6, 12, 10, 15, 5, 7, 14] in [*, 10.. => a, 10..=> b, 10..=> c,*] "a=#{a} b=#{b} c=#{c}" end #=> "a=12 b=10 c=15"