LoginSignup
3
1

More than 3 years have passed since last update.

パターンマッチ入門 Ruby 編

Posted at

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 の中に変数があれば、同時に代入も行われる。
  • 各パターンには ifunless (ガード式)を使えて、パターンマッチが成功しかつガード式も真を返すと 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の新機能・パターンマッチ(後編)

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1