RubyでStreamから渡された数値群をFixnumに変換する方法
CodeIQ、paiza等で出るプログラミングの問題では、標準入力から渡された数値データを処理する事が多いですが、標準入力経由だと基本文字列として受け取る為、計算に利用するにはプログラム内部で数値型に変換する必要があります。
この変換処理をいちいち書くのが結構おっくうだったりするのですが、Rubyでは結構簡単に処理を記述することができたので、ここにメモしておきます。
文字で与えられた数値群を一括変換
例として、標準入力から与えられた数値群を全て足した値を標準出力に出力するプログラム書いてみます。
先ずは、ダメな例から。
(a, b, c)=DATA.gets.split puts a + b + c __END__ 10 20 30
こいつを実行すると、
102030
という結果になります。単に文字を連結してるだけですね(^^;)
とりあえず動かす為の修正方法として、
puts a.to_i + b.to_i + c.to_i
とすれば目先要件は満たせるでしょうが、如何にも書き方が野暮ったいという感じがします。
to_iの処理を何回も書かないような記述方法がこれ。
(a, b, c)=DATA.gets.split.map(&:to_i) puts a + b + c __END__ 10 20 30
実行すると、
60
というわけで、それぞれ問題なく数値型に変換されています。
map(&:to_i)のやっていること
初めてこのような記述を見て、何がなんだか訳分からん状態だったので、自分なりに調べた内容でこれを要約すると、
:to_iシンボルに対するto_procメソッドの結果として返ってきたProcオブジェクトをブロックとしてmapメソッドに渡す。
という事になります。
ちょっとややこしいので細かく分解してみましょう。
先ず、mapメソッドのやっている事。
mapメソッド
mapメソッドは、要素の数だけ繰り返しブロックを実行し、ブロックの戻り値を集めた配列を作成して返します
http://ref.xaio.jp/ruby/classes/array/map
次に、(&:to_i)の部分の、「&」の意味。
ブロックの部分だけを先に定義して変数に保存しておき、後からブロック付きメソッドに渡すことも出来ます。 それを実現するのが手続きオブジェクト(Proc)です。 それをブロックとして渡すにはブロック付きメソッドの最後の引数として `&' で修飾した手続きオブジェクトを渡 します。
https://docs.ruby-lang.org/ja/2.2.0/doc/spec=2fcall.html#block
ま、mapに手続きオブジェクトを渡しているというのは確かなようです。
最後に、「&」の後に続く:to_iの意味。
これはSymbolと呼ばれるオブジェクトです。しかし「&」に続く物としては手続きオブジェクトが期待されているので型が合いません。
そのためSymbolが持つto_procメソッドで暗黙的にProcオブジェクトに変換されているのです。
https://docs.ruby-lang.org/ja/2.2.0/class/Symbol.html#I_TO_PROC
以上が、map(&:to_i)のやっている事になります。
雑感
今回のケース、正直ソースの見た目も内部でやっている事も大分ややこしいんですが、to_procが省略されているという事が発見出来れば理解するのも難しくないかなぁという感じです。