Array#eachからRubyのブロックを理解する - Qiita
へのコメントの備忘録。
実際にyieldを使ってeachメソッドを作ってみると、次のようになります。(eachメソッドを上書きしようとしたらstack level too deep (SystemStackError)になってしまいましたので、each2という名前にしています。)
class Array def each2 for i in self yield i end end end [1,2,3].each2 do |i| puts i end
これに対して。
実装を見ると for を使っておられますが、 Ruby において for の実装は each で行われているはずです。結果として each メソッドの定義に each メソッドを使ったことになり、 stack level too deep (SystemStackError) が生じると思われます。
ですので for を使わずに
class Array def each self.size.times do |i| yield self[i] end end end
とすれば良いようです。
コメントは以上です。
本当は
block_given?
付けなきゃいけないのですが手抜きバージョンのままにしてあります。 def each
直後に
return to_enum(__method__) unless block_given?
を入れればいいと思います(以下同様)。
参考:Ruby - ブロックを与えない場合にEnumeratorを返すメソッドを作る - Qiita
なお、実は self を抜いて以下のように書いても OK です。
class Array def each size.times do |i| yield [i] end end end
でも self あったほうが理解しやすいですよね。
ついでに Hash#each も書いてみました。 self 付けてますが、やはり無くてもいけます。
class Hash def each ary = self.to_a self.size.times do |i| yield ary[i].first, ary[i].last end end end
おまけ。
「プログラミング言語Ruby」p.262から引用。 next メソッドを持つモジュール(ここでは Iterable と名付けている)において each メソッドを定義する方法が載ってます。本来は Module#include に関係する記事。
module Iterable include Enumerable def each loop do yield self.next end end end
each メソッドを持っているモジュール/クラスに include Enumerable
してやると、Enumerable モジュールのメソッドが全て使えるようになります。とても便利です。
ちなみに Kernel.#loop が使っても
与えられたブロック内で StopIteration を Kernel.#raise すると ループを終了して nil を返します。
module function Kernel.#loop
ということを前提にして
列挙が既に最後へ到達している場合は、 StopIteration 例外を発生します。
instance method Enumerator#next
という Enumerator#next メソッドに倣った Iterable#next メソッドを定義しておけば、永久ループにならずコレクションの最後でループが終了します。
- 作者: まつもとゆきひろ,David Flanagan,卜部昌平(監訳),長尾高弘
- 出版社/メーカー: オライリージャパン
- 発売日: 2009/01/26
- メディア: 大型本
- 購入: 21人 クリック: 356回
- この商品を含むブログ (129件) を見る
- 作者: るびきち
- 出版社/メーカー: シーアンドアール研究所
- 発売日: 2009/05/25
- メディア: 単行本
- 購入: 24人 クリック: 263回
- この商品を含むブログ (72件) を見る