Ruby 2.7 からパターンマッチという構文が(実験的に)導入されたので、パターンマッチの概念と Ruby における構文をメモ。
実行環境
Ruby 2.7 を使用する。Docker を使っていれば以下で用意できる。
$ docker pull rubylang/ruby
$ docker run -it --rm rubylang/ruby:latest bash
パターンマッチとは?
- パターンを用いて入力データの構造を判定する機能
- パターンを用いて入力データを分解し、値を変数に束縛する機能
- case/when の強いやつ
なぜパターンマッチを導入するのか?
- 構造をもったデータを処理することに向いている
- ↑を可読性高く、安全に書ける
Ruby におけるパターンマッチ
キーワードは case
/ in
基本の文法
case expression
in pattern [if|unless condition]
#...
in pattern [if|unless condition]
#...
else
#...
end
- expression(式)が pattern(構造)と
===
で比較され、真の場合中の処理に進む。 - pattern の中に変数があれば、同時に代入も行われる。
- 各パターンには
if
やunless
(ガード式)を使えて、パターンマッチが成功しかつガード式も真を返すとin
の内容が実行される。 - どのパターンにも該当しない場合は
else
に入る。 -
else
が無く、かつどのパターンにも該当しない場合はNoMatchingPatternError
例外が発生する。
シンプルな例
実際に簡単な例をいくつか挙げてみる。
case 0
in Integer
end
Integer === 0
なので上記はマッチする。
case 1
in a
puts a # => 1
end
変数を使ったマッチもできる。
case [1, 2, 3]
in [1, 2, a]
puts a # => 3
end
配列を使ったパターンでは、要素の個数と値が一致しているかをチェックする。
JSON のパース
error = JSON.parse(response.body)["error"]
if error.present?
message = body["error"]['message']
logger.error(message)
end
通常はキーの存在を確認しながら見ていくが、
case JSON.parse(response.body)
in { error: { message: message } }
logger.error(message)
end
パターンマッチを使うとシンプルかつ可読性高く書ける。
クラス名の分割
splitted_class_name = "Foo::BarController".split('::')
case splitted_class_name
in [/Foo|Baz/ => namespace, /.+Controller/]
puts namespace # => "Foo"
end
===
で判定するため、パターンに正規表現を使うこともできる。
また、マッチしないケースがくると例外が発生するので予期せぬ動きを防げる。(Controller ではないクラス名が入ったときなど)
パターン紹介
上で例として挙げたパターンには名前がついており、以下で紹介する。
1. Value パターン
パターンとして単一のリテラルを指定。
case 0
in 0
end
2. Variable パターン
パターンとして変数を指定。必ずマッチする。
case 0
in a
end
3. Alternative パターン
複数のパターンを |
で並べたものをパターンとして指定。
case 0
in 0 | 1 | 2
end
4. As パターン
マッチ対象のオブジェクトを変数に代入できる。パターンと変数をハッシュロケット =>
で区切る。
case 0
in Integer => a
end
5. Array パターン
複数のパターンを配列として指定。Array オブジェクトに限らず、deconstruct
に応答できればマッチできる。
case [0, 1, 2]
in [0, *a]
end
6. Hash パターン
パターンをハッシュ形式のキーで指定。Hash オブジェクトに限らず、deconstruct_keys
に応答できればマッチできる。
case { a: 0, b: 1 }
in { a: 0, b: 1 }
end
参考
Pattern matching - New feature in Ruby 2.7
n月刊ラムダノート Vol.1, No.3(2019)
サンプルコードでわかる!Ruby 2.7の新機能・パターンマッチ(前編)
サンプルコードでわかる!Ruby 2.7の新機能・パターンマッチ(後編)