inject の罠

Problem 1 - Project Euler

1000 未満の 3 または 5 の倍数を足し込む問題.ここで早速以前習得した inject を使ってみる.

p (1...1000).inject(0) { |s, i|
  if i % 3 == 0 || i % 5 == 0 then s += i end
}

実行すれば分かるがエラーになる.

p (1...1000).inject(0) { |s, i|
  if i % 3 == 0 || i % 5 == 0 then s += i end
  s
}

正しくはこう.

どういうことかと言うと,恐らく inject に渡したクロージャは毎回第 1 引数 (ここでは s) にその評価された値 (つまり戻り値.Ruby では最後に評価した値も戻り値扱いになる.) を代入する.で,前者の書き方では 3 または 5 の倍数で無いときには何も評価されない (nil が返る).よって inject のループの 1 回目のステップ終了時に s には nil が代入される.そこで nil += 3 が実行される段階においてエラーが起こる.

よってこれを解決するには毎回 s をきちんと評価すれば良い.よって最後に s とだけ書いておく.

現時点の私の直感では,この s は明らかに余分である.inject の仕様を回避するための処置である.何かこの s を書かなくて良い方法 (inject 以外のもっと適切な名前のメソッド) があれば知りたい.