はじめに
たまに 横浜へなちょこプログラミング勉強会 で出題された問題を解くことがあり、解いた後は他の参加者の回答を見るのだが、参加者のRubyの使用率が結構高い。普段の仕事ではC++やJavaScriptを使う私にとって、Rubyのコードを読むのはツラかったりする。「Rubyは仕事で使うわけでもないし、まぁだいたい読めれば良いか〜」と割り切っても良いのだが、できないよりはできた方が良いよね!ってことで勉強することにした。
以下の理由から、勉強に使用する書籍は プロを目指す人のためのRuby入門 にした。
- アマゾンのレビューが高評価だったこと
- 著者が書かれた記事(QiitaやSoftware Designなど)が非常に分かりやすかったこと
本記事は『プロを目指す人のためのRuby入門』を読んで、どの程度Rubyを読み書きできるようになったかの確認結果をまとめたものである。書籍のレビューではないため、どんな書籍か気になる方は 著者のブログ 等をご確認ください。
本記事で使用するRuby, minitestのバージョン
- Ruby: 2.5.2
- minitest: 5.10.3
『プロを目指す人のためのRuby入門』を読む前のレベル
Rubyをネットで調べながら書いたことはあるが、書籍等で体系的に学んだことはないレベル...と言っても伝わらないと思うので、過去に書いたコードをいくつか晒す。
一応Rubyっぽく書けている(部分もある)と思うが、例えば map(&:to_i)
が動作する仕組みについて知らないし、 ||
と or
の違いも優先順位が違うくらいの理解しかない。
どの程度Rubyのコードを読めるようになったか?
『プロを目指す人のためのRuby入門』を一読したので、 私が読めなかった「横浜へなちょこプログラミング勉強会」の回答 を読んでみる。
ちなみに問題は とあるカードゲーム 2018.10.6 から確認できる。
コードリーディング
コードリーディングでは コードの各行がどんな処理をやっているかを追うだけで、アルゴリズムの解説は行わない (本記事はあくまでRubyの記事のため)。
コードの骨格部分
このコードの骨格は以下の部分。
Enumerator.new{|y| ... }.max || ?-
まず前半部分 Enumerator.new{|y| ... }.max
は生成した Enumerator
オブジェクトの each
を呼び出し、ブロック部分(上記コードの「...」の部分)の実行結果から最大値を取り出す処理である。上記コードで実際に呼び出しているのは max
だが、 Enumerator
がインクルードしている Enumerable
の max
は each
を用いて定義されているため、結局 each
が呼ばれる。
- Ruby 2.5.0 リファレンスマニュアル > ライブラリ一覧 > 組み込みライブラリ > Enumeratorクラス > new
- Ruby 2.5.0 リファレンスマニュアル > ライブラリ一覧 > 組み込みライブラリ > Enumerableモジュール
後半部分 (前半の実行結果) || ?-
は、前半の実行結果が偽の場合は -
を返すという、プログラムの仕様 「あがり」でない場合は -
を出力すること。 を実装した部分である。
続いて Enumerator
オブジェクトに指定したブロック部分を見ていく。
Enumerator
オブジェクトに指定したブロック部分
(f=->c,s=0{ ... })[input.split(?,).map{|s|s.tr("A-G","0-6").to_i}]
これはラムダリテラル( ->
)を使って作成したラムダオブジェクトである。このコードは入力文字列(input)を本プログラムに適合する形に変換して、ラムダオブジェクト( f
)の引数( c
)に渡している。
引数部分(上記コードの []
の部分)が長め?のメソッドチェーンになっていて(Ruby初心者には)分かりにくいが、 input
は入力値(問題で提供されている "A1,A2,A3,A4,B3,C3,D5,E5"
などのテストケース)で、それを
-
?,
で区切り - 区切った各文字列(例えば
A1
やA2
など)のアルファベットA〜Gを数字0〜6に置き換え - 置き換えた文字列をInteger型のオブジェクトに変換
している。例えば入力文字列が "A1,A2,A3,A4,B3,C3,D5,E5"
の場合、変換結果は [1, 2, 3, 4, 13, 23, 35, 45]
の配列になる。
最後にラムダオブジェクト本体の処理(上記コードの「...」部分)を見ていく。
ラムダオブジェクト本体の処理コード
[1,10].each{|d|
c.each{|b|
b.step(by:d).with_index.with_object(c.dup){|(t,i),c2|
c2.delete(t) or break
c2.empty? ? y<<s+i*i : f[c2,s+i*i] if i>0
}
}
}
上記コードは d, c, b, t, i, c2, s
など色々な変数が出てきてややこしいが、単なるループ処理なので各変数が何を指しているかを把握できれば問題ない。実際の処理は上記コードの4, 5行目。
c2.delete(t) or break
4行目は配列 c2
に要素 t
が無い場合はループを抜ける処理である。ここで『プロを目指す人のためのRuby入門』を読む前の私のように「 or
は ||
と優先順位が違うだけ」程度の理解だと c2.delete(t) or break
の様なコードを見て「え!?」ってなる。 or
はブール式ではなく制御フローのためにあるというのは書籍で勉強するまで知らなかった。
続いて5行目
c2.empty? ? y<<s+i*i : f[c2,s+i*i] if i>0
は、0 < i
の時配列 c2
が空なら Yielderオブジェクト
にスコアの正解候補を格納し、空でなければラムダオブジェクトを再帰呼び出しするコードである。
コードリーディングしてみて
基本的に『プロを目指す人のためのRuby入門』で得た知識で読むことができたと思う。
一部リファレンスを参照したが、「はじめに」で紹介した著者のブログにある通り、『プロを目指す人のためのRuby入門』はリファレンス本では無いので良いかなと思う(実際にググっても載っていない情報の方が読み手としては嬉しい)。
配列やハッシュのメソッドをあれこれ1つずつ説明するようなリファレンス本にはしていません。
いや、実は最初の原稿ではたくさん説明していたんですが(たとえばzipメソッドの使い方とかね)、ページ数も大幅に増えるし、「そもそもこんなの、ググったら公式ドキュメントに載ってるじゃん?」と思ったので、途中でばっさりカットしました。
どの程度Rubyを書けるようになったか?
ある程度Rubyプログラムを読めるようになったことが確認できたので、続いてどの程度書けるようになったのかを確認する。題材は「横浜へなちょこプログラミング勉強会」で出題された 増築の果ての隣室 2018.11.3 を使う。
Rubyプログラミング
私の書いたコードへのリンクを貼っておく。
Rubyプログラミングしてみて
勉強前のコードと見た目は大差ない(極端にRubyっぽいコードになったとかは無い)かな〜と思うが、ググった知識でなんとなく書く部分はなくなったと思う。
最後に
Rubyを少し触った程度の人が入門書として『プロを目指す人のためのRuby入門』を一読した結果、コードの読み書き共にレベルアップしたなと実感できた。少なくとも「はじめに」で書いた課題(勉強会参加者のRubyコードを読むのがツライときがある)は解決されるまでに成長できた。