Enumerator::Lazyの継承関係を理解できていませんでした。。
以下にまとめました。
①Enumerator::Lazy#takeで指定した数のEnumerator::Lazyオブジェクトを取得
p (1..Float::INFINITY).lazy.map { |i| i ** 2}.take(3)
# =>#<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: 1..Infinity>:map>:take(3)>
②Enumerator::Lazy#forceで配列を取得
p (1..Float::INFINITY).lazy.map { |i| i ** 2}.take(3).force
# =>[1, 4, 9]
③配列に対してEnumerable#injectの実行
p (1..Float::INFINITY).lazy.map { |i| i ** 2}.take(3).force.inject(0,&:+)
# => 14
④Enumerator::Lazyに対してEnumerable#injectの実行
③と同じ結果。Enumerator::Lazy#force
は不要であると分かる。
p (1..Float::INFINITY).lazy.map { |i| i ** 2}.take(3).inject(0,&:+)
# => 14
まとめ
Enumerator::Lazyオブジェクトに対してinjectメソッドを呼ぶと例外が発生するかと思っていましたが間違いでした。
Enumerator::Lazy#forceで配列に戻してからでないと、injectメソッドを使用することができないと思っていましたがそんなことはなく、Enumerator::LazyはEnumerableモジュールを継承しているので、配列に戻さなくてもそのままEnumerable#injectを使用できました。
参考リンク
instance method Enumerator::Lazy#take
take(n) -> Enumerator::Lazy
Enumerable#take と同じですが、配列ではなくEnumerator::Lazy を返します。
n が大きな数 (100000とか) の場合に備えて再定義されています。 配列が必要な場合は Enumerable#first を使って下さい。
[PARAM] n:
要素数を指定します。
[EXCEPTION] ArgumentError:
n に負の数を指定した場合に発生します。
[SEE_ALSO] Enumerable#take
instance method Enumerator::Lazy#force
force(*args) -> [object]
全ての要素を含む配列を返します。Lazy から実際に値を取り出すのに使います。
Enumerable#to_a
のエイリアスです。
instance method Enumerable#inject
以下の通り、Enumerator::Lazy
はEnumerableモジュール
を継承しているのでEnumerable#inject
を使用できる。
pry(main)> Enumerator::Lazy.ancestors
=> [Enumerator::Lazy, Enumerator, Enumerable, Object, PP::ObjectMixin, Kernel, BasicObject]
参考にしたツイート
以下のツイートにインスパイアされて記事を書きました。
参考: 武田哲也さんのRuby技術者認定試験受験記
#RubyGold こんな感じのコードが出た。結果どうなる?と。 (1..Float::INFINITY).lazy.map {|i| i**2 }.take(3).inject(0) {|sum,i| sum += i }
— 武田哲也 (@__takeda) 2015, 1月 11
#RubyGold (続き)lazy も take も初めて見たが、lazyは遅延評価的なやつかな、やりたいのは「1~3の冪乗の合計」だろう、1+4+9=14 →正解。 これらはRuby2.0で導入された。 http://t.co/NhDysp1LQM
— 武田哲也 (@__takeda) 2015, 1月 11
補足: Enumerator::Lazy は2.0からですが、 Enumerable#take は1.8.7から入っています。
http://ref.xaio.jp/ruby/classes/enumerable/take