16 Jun 2010

injectを実装してみた

Emunarableのinjectを自分で実装してみた。ほんとそれだけなんだけど、こういうことを何もみないでやってみてって言われるとパッと書けないんで練習。

最初これでエラーになってました。
  1. class Range    
  2.   def my_inject(sum, &block)    
  3.     self.collect{|v| sum = block(sum, v)}.last    
  4.   end    
  5. end    
  6.     
  7. p (1..3).inject(1){|sum, n| sum + n}    
  8. p (1..3).my_inject(1){|sum, n| sum + n}    

3行目のblockでundefined methodが出ます。blockはProc型のインスタンスだからです。正しくは block.call(sum, v)。yieldでの呼び出しと時々ごっちゃになるんですよね。

- yieldでの呼び出し
  1. class Range    
  2.   def my_inject(sum)    
  3.     self.collect{|v| sum = yield(sum, v)}.last    
  4.   end    
  5. end  
  6.   
  7. p (1..3).inject(1){|sum, n| sum + n} #=> 7  
  8. p (1..3).my_inject(1){|sum, n| sum + n} #=> 7  


my_injectにyieldがない場合は単に無視され、my_injectの中が評価され返されます。

  1. class Range    
  2.   def my_inject(sum)    
  3.     1  
  4.   end    
  5. end  
  6.   
  7. p (1..3).inject(1){|sum, n| sum + n} #=> 7  
  8. p (1..3).my_inject(1){|sum, n| sum + n} #=> 1  



- 蛇足
これを書いていたときに(1..3)とするところを[1.3]としていてsum + nでエラーになってしまいました。[1..3]は配列の中にRange型のインスタンスが格納されているだけなので sum + nは 1 + 1..3となっていたんですね。

()でrangeを囲む理由は単に1..3.inject(){}と書けないからだと思います。

rubyでは読めるけど見ないで書けないと言う場面によく遭遇しますね。英語みたいですね。書くことは書くことでしか上達しないです。JavaやC#をやってたときは標準ライブラリのメソッド名が長すぎたりするのでeclipceのオートコンプリートに頼ったりAPIドキュメントを見たりしてあまり覚える必要性を感じませんでしたが、rubyの世界ではできるだけ何も見ないで直感的にコードを書ける能力がより重視されている気がします。