神山.rb開催おめでとうございます
このスライド自体のリポジトリは kaosf/20141108-kamiyamarb-slide にあります
間違いを発見したら Issue あるいは PullRequest を下さい
Rubyistの皆さんこんなコード書いてませんか
sum = 0 [1, 2, 3].each do |i| sum += i end
今すぐそんなコードは投げ捨てよう
他にも以下のような例
str = "" ["a", "b", "c"].each do |i| str += (i + ",") end
reduceを使おう
同じ機能で別名のinjectでも良い
sum = [1, 2, 3].reduce(0) { |s, i| s + i } str = ["a", "b", "c"].inject("") { |s, i| s + i + "," }
メソッドの引数を初期値として,リストの全要素に対して,ブロックの第一引数が初期値に,第二引数が各要素になりながら,戻り値を次のイテレーションの第一引数にし,ループする.
最終的な戻り値は最後のブロックの戻り値.
なにいってだこいつ
[1, 2, 3].reduce(0) { |s, i| s + i }
まず 0 がブロックの s に1 が i に渡る
s + i は 0 + 1 なのでブロックの戻り値は 1
それが次の s になる
[1, 2, 3].reduce(0) { |s, i| s + i }
が
[2, 3].reduce(1) { |s, i| s + i }
という状態になる
[1, 2, 3].reduce(0) { |s, i| s + i } [2, 3].reduce(1) { |s, i| s + i } [3].reduce(3) { |s, i| s + i }
そして最後の s + i が 6 になりそれが結果になる
(((0 + 1) + 2) + 3)
これが計算されるわけである
これは所謂 foldl である
[].reduce(10) { |s, i| s + i } #=> 10
初期値がそのまま返る
前のページで「最後のブロックの戻り値が結果になる」と言ったな
あれは嘘だ
[1, 2, 3].reduce(0) { |s, i| s + i } [2, 3].reduce(1) { |s, i| s + i } [3].reduce(3) { |s, i| s + i } [].reduce(6) { |s, i| s + i } #=> 6
例外的にリストの第一要素が初期値になる
[1, 2, 3].reduce { |s, i| s + i } [2, 3].reduce(1) { |s, i| s + i } [3].reduce(3) { |s, i| s + i } [].reduce(6) { |s, i| s + i } #=> 6
[1, 2, 3].reduce(0, :+) #=> 6
これでも大丈夫
methodが指定されたとすると…
[1, 2, 3].reduce(0) { |s, i| s.method(i) }
:method が指定された場合
[1, 2, 3].reduce(0) { |s, i| s.send(:method, i) }
class Fixnum def add10(x) 10 * self + x end end [1, 2, 3].reduce(0, :add10) #=> 123
s = 'shokichi' # ... for i in ['youso', 'element', 1, 2] # ... s.ni_nanika_suru(i) end nantoka_variable = brushup(s)
ペロ…これは…クソコード!
["a", "b", "c"].join(",") #=> "a,b,c"
メソッドが用意されていることも多い
多分さっきの例で本当にやりたかったことはこうだと思う
初期値設定してループ回して書き換えしていくのはやめよう
面倒・間違えやすい・コードの行数も増える
色々なメソッドがあるのでちゃんと調べよう