エンジニアの澤田です。
この連載では、社内のRuby (on Rails)コードで気になった箇所の問題点やそこから発展して関連事項を議論しています。
5回目の「マネーフォワード社内PRに見られるRubyの書き方について - (5) 文の環境」では文の環境を考察しました。 今までは1記事1テーマで書いていましたが、毎回長くなりがちだったので、今回からは、1つのテーマでも長くなるときは短く分割し、復数の記事にします。今回から受け渡しのパターンマッチングについて扱います。
【バックナンバー】
- マネーフォワード社内PRに見られるRubyの書き方について - (1) 配列の生成
- マネーフォワード社内PRに見られるRubyの書き方について - (2) ハッシュの生成
- マネーフォワード社内PRに見られるRubyの書き方について - (3) 文字列の生成や検証
- マネーフォワード社内PRに見られるRubyの書き方について - (4) 真理値
- マネーフォワード社内PRに見られるRubyの書き方について - (5) 文の環境
オブジェクトを渡して定数や変数で受けるとき、Rubyでは1対1で行うだけでなく、多重代入、分解、統合、不要であることのマーキングなどの多様な機能を使うことが出来ます。これらはまとめて「パターンマッチング」と呼ばれています。 今回の6回目は、このうち、「オブジェクトを渡すときのパターンマッチングの要素」についてまとめます。1
7回目は逆に「定数や変数で受けるときのパターンマッチングの要素」のまとめ、8回目は6, 7回目に関係する見かけたコードの紹介とそれについての議論をします。
オブジェクトを渡すときのパターンマッチングの要素
1. 単一のオブジェクト
リテラルを使うか定数や変数でオブジェクトを表します。連載5回目で扱った文の環境全てに現れます。
{a: 1} foo
2. 配列リテラルの[]
の補完
次の環境で配列リテラルの[]
を省略することが出来ます。
制御構造の
break
,next
,return
の引数 (連載5回目の⑤a)break "foo", "bar"
代入式の右辺 (⑤c)
x = 1, 2
y, z = 3, 4
```
### 3. ハッシュリテラルの`{}`の補完
次の環境でハッシュリテラルの`{}`を省略することが出来ます。
* 配列リテラルの最後の要素 (⑤f)
```ruby
["bar", "a" => 1, "b" => 2]
```
また、Ruby 3になるまでは、次の環境でハッシュリテラルの`{}`を省略することが出来ます。
* メソッドや`super`、`yield`の最後の実位置引数 (⑤d, e)
```ruby
foo("bar", "a" => 1, b: 2)
```
しかし、この規則により、実引数の最後がハッシュの位置引数なのか、それともキーワード引数があるのかの区別が曖昧であったり複雑であったりしたので、Ruby 3ではこの補完規則は廃止される予定になっています。
### 4. `*foo`による配列の分解
以下に相当する位置:
1. 制御構造`rescue`, `when`の引数のうちの連続した任意の個数 (= 1個の場合は⑤a)
```ruby
rescue AException, *foo, BException
```
2. メソッドの実位置引数のうちの連続した任意の個数 (= 1個の場合は⑤d, e)
```ruby
bar("a", *foo, "b")
```
3. 配列リテラルの要素のうちの連続した任意の個数 (= 1個の場合は⑤f)
```ruby
["a", *foo, "b"]
```
で`*`をオブジェクト`foo`の前に付けると、暗黙的に`foo`のメソッド`to_a`の呼び出しが試みられ、
* `to_a`が未定義なら`*foo`は`foo`に展開され、
* `to_a`が配列以外を返せば、タイプエラーが発生し、
* `to_a`が配列を返せば、`*foo`はその配列の要素の並びに展開されます。
### 5. `**foo`によるハッシュの分解
以下に相当する位置:
1. メソッドの実キーワード引数と値の対のうちの連続した任意の個数
```ruby
bar(baz, a: "b", **foo, c: "d")
```
2. ハッシュリテラルのキーと値の対のうちの連続した任意の個数
```ruby
{"a" => "b", **foo, "c" => "d"}
で**
をオブジェクトfoo
の前に付けると、暗黙的にfoo
のメソッドto_hash
の呼び出しが試みられ、
to_hash
が未定義ならタイプエラーが発生し、to_hash
がハッシュ以外を返せば、タイプエラーが発生し、to_hash
がハッシュを返せば、**foo
はそのハッシュのキーと値を各各キーワード引数とその値とするものに展開されます。
ただし、Ruby 2.7になるまでは、
to_hash
がハッシュを返し、それがシンボル以外のキーを持てば、タイプエラーが発生します。
**
によるハッシュの分解については、バグっぽい挙動がいくつか知られています。その多くは「ハッシュリテラルの{}
の補完」の節で述べた省略規則やキーワード引数とメソッドの最後のハッシュ引数との区別が曖昧であったり複雑であったりすることに起因しています。Ruby 3ではこの省略規則がなくなり、また区別が明確になることで、このような問題がなくなると期待されます。2
6. &foo
によるProc
オブジェクトの変換
以下に相当する位置:
メソッドの最後の実引数
bar("baz", &foo)
で&
をオブジェクトfoo
の前に付けると、暗黙的にfoo
のメソッドto_proc
の呼び出しが試みられ、
to_proc
が未定義ならタイプエラーが発生し、to_proc
がProc
オブジェクト以外を返せば、タイプエラーが発生し、to_proc
がProc
オブジェクトを返せば、&foo
はfoo
をそのメソッドに付随するコードブロックに展開されます。
まとめ
今回はオブジェクトを受け渡すときのパターンマッチングについて、渡す側の文法的要素についてまとめました。次回のは受け取り側を扱います。
最後に
マネーフォワードでは、エンジニアを募集しています。 ご応募お待ちしています。
【採用サイト】 ■マネーフォワード採用サイト ■Wantedly
【マネーフォワードのプロダクト】 ■お金の見える化サービス 『マネーフォワード ME』 iPhone,iPad Android
■ビジネス向けバックオフィス向け業務効率化ソリューション 『マネーフォワード クラウド』
■金融商品の比較・申し込みサイト 『Money Forward Mall』
■本業に集中できる新しいオンライン融資サービス 『Money Forward BizAccel』
- この記事はRuby開発者の一人である卜部昌平氏に目を通してもらいました。記事に間違いがあれば、それは筆者の責に帰するものです。↩
- https://bugs.ruby-lang.org/issues/14183 参照↩