フィヨルドブートキャンプではプラクティスとしてボウリングのスコア計算プログラムを作成します。提出物に合格をもらった後、他の受講生のコードを読んでいて初めて知ったメソッドにeach_with_object
というメソッドがあります。これはめちゃくちゃ便利じゃね?(自分が知らなかっただけ)と思った次第ですので、忘備録として、また学習ノートとしてeach_with_object
の使い方を記しておきたいと思います。フィヨルド生には若干のネタバレにはなるかもしれませんが、あんまり影響しないよう断片的なコードにしています。
ボウリングのスコア計算プログラムとは
実行例は以下の通りターミナルでコマンドラインの引数としてカンマ区切りのスコアを渡すと得点が出力されます。
$ ./bowling.rb '6,3,9,0,0,3,8,2,7,3,X,9,1,8,0,X,6,4,5' 139
上記でストライクは'X'
として表されています。またスペアは3,7
などの数字で合計10になるときです。
また、ボウリングはスコアの計算方法が改正されていますが、今回は現行のカレントフレームスコア方式ではなく、旧のスコア計算方法で実装しています。
詳しくはコチラ:ボウリングの新スコア計算方法!カレントフレームスコアシステムで遊んでみたよ
自分が書いたコード
def convert_to_value(score) shots = [] score.split(',').each do |shot| if shot == 'X' && shots.size < 18 shots << 10 << 0 elsif shot == 'X' shots << 10 else shots << shot.to_i end end shots end
上のコードで記述したメソッドは引数で'6,3,9,0,0,3,8,2,7,3,X,9,1,8,0,X,6,4,5'
といった文字列のスコアを受け取る想定をしています。
カンマで区切られている数字やマークを数値に変換し、一つの配列として出力しています。そのとき最終フレーム以外のストライクのマークX
は10,0
として処理し、最終フレームのストライクのマーク'X'は単に10
としています。
実際に引数を渡してみると以下のようになります。
convert_to_value('6,3,9,0,0,3,8,2,7,3,X,9,1,8,0,X,6,4,5') => [6, 3, 9, 0, 0, 3, 8, 2, 7, 3, 10, 0, 9, 1, 8, 0, 10, 0, 6, 4, 5]
each_with_indexを使って書くと
def convert_to_value(score) score.split(',').each_with_object([]) do |shot, shots| if shot == 'X' && shot.size < 18 shots << 10 << 0 elsif shot == 'X' shots << 10 else shots << shot.to_i end end end
さて、何が変わっているでしょうか
- shots = [] - score.split(',').each do |shot| + score.split(',').each_with_object([]) do |shot, shots|
【変更前】空の配列を用意し、条件に合うものをその配列に突っ込んでいました。
【変更後】each_with_objectを使ったことにより、引数に空の配列を渡すことで、ブロック変数の第二引数である
shots
にセットされます。
- shots end
また【変更前】は最後にshotsを記述することで配列shots
を戻り値としていたのですが、【変更後】はeach_with_object
の戻り値がshotsなためコードがスッキリしました。
まとめ
空の配列(ハッシュ)を用意して、何らかの条件によって配列に入れていくような操作はeach_with_object
が使える。またメソッドの戻り値は引数のオブジェクトになる。
つまり、配列の用意と戻り値で2行ほどお得!(ちょっと俗っぽい言い方)
参考:https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/each_with_object.html