パターンマッチの基本
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
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
case {name: 'Prius', engine: '98ps', motor: '72ps'}
in {name: , engine: , motor: }
puts "#{name} #{engine} #{motor}"
end
パターンマッチの利用パターン
パターンマッチを利用するパターンは下記の7パターン。
value
パターンの特徴は、in
節に数値や文字列を直接指定できるという点。case
節の式とin
節の値が等しければ実行される。
country = 'italy'
case country
in 'japan'
'こんにちは'
in 'italy' then 'Ciao'
end
message =
case country
in 'japan'
'こんにちは'
in 'italy'
'Ciao'
end
message
variableパターン
variable
パターンの特徴は、in
節のパターンに変数を書いて、ローカル変数の宣言と代入を同時に行うという点。『パターンマッチの基本』の2.の内容にあたる。事前に定義した変数などをin
節で使用できる。その際は、^
を使う。
alise, name = 'Alise', 'Alise'
case name
in ^alise
'Hey, Alise'
end
arrayパターン
array
パターンの特徴は、in
節に[]を使って配列の構造パターンを指定するという点。『パターンマッチの基本』の2.の配列の内容にあたる。入れ子関係になっていてもOK。
case [1, [2, 3]]
in [a, b]
"a=#{a}, b=#{b}"
end
case [1, 2, 3]
in [a, *rest]
"a=#{a}, rest=#{rest}"
end
hashパターン
hash
パターンの特徴は、in
節に{}
を使ってハッシュの構造パターンを指定するという点。値に変数を指定すると、その変数に対応する値が格納される。キーの順番はパターンマッチに影響しない。
hash
パターンは、ハッシュの各要素がin
節で指定したパターンに部分位置していればマッチしたとみなす。よってin
の順番に気をつけないと、永久に実施されないメソッドができてしまう。ただ、in
節に{}
を書いた場合は、『空のハッシュに完全一致』がマッチの条件になる。
基本的には、『パターンマッチの基本』の2.のhashの内容にあたる。
**
をつけることで、残りカスをまとめて変数に入れられる。
case{name: 'Alise', age: 20, gender: :female}
in {name: 'Alise', **rest}
"rest=#{rest}"
end
asパターン
as
パターンの特徴は、パターンマッチでマッチしたオブジェクトを変数に代入するという点。=> 変数名
と書いてあげると、マッチしたオブジェクトを代入できる。
case {name: 'Alise', age: 20, gender: :female}
in {name: String => name, age: 18.. => age}
"name=#{name}, age=#{age}"
end
alternative
パターンの特徴は、2つ以上のパターンを指定し、どれか1つにマッチすればマッチしたとみなす点。パターンを|
で連結する。
case 2
in 0 | 1 | 2
'matched'
end
case [2021, 4, 1]
in [_,_] | [_, _, _]
'matched'
end
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