class Enumerator::Lazy

[edit]

要約

map や select などのメソッドの遅延評価版を提供するためのクラス。

動作は通常の Enumerator と同じですが、以下のメソッドが遅延評価を行う (つまり、配列ではなく Enumerator を返す) ように再定義されています。

Lazyオブジェクトは、Enumerable#lazyメソッドによって生成されます。

Lazyから値を取り出すには、Enumerator::Lazy#force または Enumerable#first を呼びます。



# 二乗して偶数になるような整数を、小さい方から5個表示する
p 1.step.lazy.select{|n| (n**2).even?}.first(5)
# LTSV (http://ltsv.org/) 形式のログファイルから検索を行う
# Enumerator::Lazy#map は配列ではなく Enumerator を返すため、
# 巨大な配列を確保しようとしてメモリを使い切ったりはしない
open("log.txt"){|f|
  f.each_line.lazy.map{|line|
    Hash[line.split(/\t/).map{|s| s.split(/:/, 2)}]
  }.select{|hash|
    hash["req"] =~ /GET/ && hash["status"] == "200"
  }.each{|hash|
    p hash
  }
}

目次

特異メソッド
インスタンスメソッド

継承しているメソッド

Enumeratorから継承しているメソッド
Enumerableから継承しているメソッド

特異メソッド

new(obj, size=nil) {|yielder, *values| ... } -> Enumerator::Lazy[permalink][rdoc][edit]

Lazy Enumerator を作成します。Enumerator::Lazy#force メソッドなどによって列挙が実行されたとき、objのeachメソッドが実行され、値が一つずつブロックに渡されます。ブロックは、yielder を使って最終的に yield される値を指定できます。

Enumerable#filter_map と、その遅延評価版を定義する例

module Enumerable
  def filter_map(&block)
    map(&block).compact
  end
end

class Enumerator::Lazy
  def filter_map
    Lazy.new(self) do |yielder, *values|
      result = yield *values
      yielder << result if result
    end
  end
end

1.step.lazy.filter_map{|i| i*i if i.even?}.first(5)
    # => [4, 16, 36, 64, 100]
[EXCEPTION] ArgumentError:
引数を指定しなかった場合、ブロックを指定しなかった場合に発生します。

[SEE_ALSO] Enumerator.new

インスタンスメソッド

chunk {|elt| ... } -> Enumerator::Lazy[permalink][rdoc][edit]
chunk(initial_state) {|elt, state| ... } -> Enumerator::Lazy

Enumerable#chunk と同じですが、配列ではなく Enumerator::Lazy を返します。



1.step.lazy.chunk{ |n| n % 3 == 0 }
# => #<Enumerator::Lazy: #<Enumerator: #<Enumerator::Generator:0x007f8bf18118f0>:each>>

1.step.lazy.chunk{ |n| n % 3 == 0 }.take(5).force
# => [[false, [1, 2]], [true, [3]], [false, [4, 5]], [true, [6]], [false, [7, 8]]]

[SEE_ALSO] Enumerable#chunk

chunk_while {|elt_before, elt_after| ... } -> Enumerator::Lazy[permalink][rdoc][edit]

Enumerable#chunk_while と同じですが、Enumerator ではなく Enumerator::Lazy を返します。

[EXCEPTION] ArgumentError:
ブロックを指定しなかった場合に発生します。
map {|item| ... } -> Enumerator::Lazy[permalink][rdoc][edit]
collect {|item| ... } -> Enumerator::Lazy

Enumerable#map と同じですが、配列ではなくEnumerator::Lazy を返します。

[EXCEPTION] ArgumentError:
ブロックを指定しなかった場合に発生します。


1.step.lazy.map{ |n| n % 3 == 0 }
# => #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator: 1:step>>:map>

1.step.lazy.collect{ |n| n.succ }.take(10).force
# => [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

[SEE_ALSO] Enumerable#map

flat_map {|item| ... } -> Enumerator::Lazy[permalink][rdoc][edit]
collect_concat {|item| ... } -> Enumerator::Lazy

ブロックの実行結果をひとつに繋げたものに対してイテレートするような Enumerator::Lazy のインスタンスを返します。


["foo", "bar"].lazy.flat_map {|i| i.each_char.lazy}.force
#=> ["f", "o", "o", "b", "a", "r"]

ブロックの返した値 x は、以下の場合にのみ分解され、連結されます。

  • x が配列であるか、to_ary メソッドを持つとき
  • x が each および force メソッドを持つ (例:Enumerator::Lazy) とき

それ以外のときは、x は分解されず、そのままの値として使われます。


[{a:1}, {b:2}].lazy.flat_map {|i| i}.force
#=> [{:a=>1}, {:b=>2}]
[EXCEPTION] ArgumentError:
ブロックを指定しなかった場合に発生します。

[SEE_ALSO] Enumerable#flat_map

drop(n) -> Enumerator::Lazy[permalink][rdoc][edit]

Enumerable#drop と同じですが、配列ではなくEnumerator::Lazy を返します。

[PARAM] n:
要素数を指定します。
[EXCEPTION] ArgumentError:
n に負の数を指定した場合に発生します。


1.step.lazy.drop(3)
# => #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator: 1:step>>:drop(3)>

1.step.lazy.drop(3).take(10).force
# => [4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

[SEE_ALSO] Enumerable#drop

drop_while {|item| ... } -> Enumerator::Lazy[permalink][rdoc][edit]

Enumerable#drop_while と同じですが、配列ではなくEnumerator::Lazy を返します。



1.step.lazy.drop_while { |i| i < 42 }
# => #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator: 1:step>>:drop_while>

1.step.lazy.drop_while { |i| i < 42 }.take(10).force
# => [42, 43, 44, 45, 46, 47, 48, 49, 50, 51]

[SEE_ALSO] Enumerable#drop_while

eager -> Enumerator[permalink][rdoc][edit]

自身を遅延評価しない Enumerator に変換して返します。



lazy_enum = (1..).each.lazy

# select が遅延評価されるので終了する
p lazy_enum.class # => Enumerator::Lazy
p lazy_enum.select { |n| n.even? }.first(5)
# => [2, 4, 6, 8, 10]

# select が遅延評価されないので終了しない
enum = lazy_enum.eager
p enum.class # => Enumerator
p enum.select { |n| n.even? }.first(5)
to_enum(method = :each, *args) -> Enumerator::Lazy[permalink][rdoc][edit]
enum_for(method = :each, *args) -> Enumerator::Lazy
to_enum(method = :each, *args) {|*args| block} -> Enumerator::Lazy
enum_for(method = :each, *args) {|*args| block} -> Enumerator::Lazy

Object#to_enum と同じですが、Enumerator::Lazy を返します。

to_enum は「ブロック付きで呼ぶとループを実行し、ブロックを省略した場合は Enumerator を返す」ようなメソッドを定義するときによく使われます。このときに lazy 性が正しく引き継がれるように、Lazy#to_enum は素のEnumerator ではなく Enumerator::Lazy を返すようになっています。



module Enumerable
  # 要素をn回ずつ繰り返すメソッド
  # 例:[1,2,3].repeat(2)  #=> [1,1,2,2,3,3]
  def repeat(n)
    raise ArgumentError if n < 0
    if block_given?
      each do |*val|
        n.times { yield *val }
      end
    else
      to_enum(:repeat, n)
    end
  end
end

r = 1..10
p r.map{|n| n**2}.repeat(2).first(5)
#=> [1, 1, 4, 4, 9]

r = 1..Float::INFINITY
p r.lazy.map{|n| n**2}.repeat(2).first(5)
#=> [1, 1, 4, 4, 9]

# Lazy#to_enum のおかげで、repeat の返り値は
# もとが Enumerator のときは Enumerator に、
# もとが Lazy のときは Lazy になる
select {|item| ... } -> Enumerator::Lazy[permalink][rdoc][edit]
find_all {|item| ... } -> Enumerator::Lazy
filter {|item| ... } -> Enumerator::Lazy

Enumerable#select と同じですが、配列ではなくEnumerator::Lazy を返します。

[EXCEPTION] ArgumentError:
ブロックを指定しなかった場合に発生します。


1.step.lazy.find_all { |i| i.even? }
# => #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator: 1:step>>:find_all>

1.step.lazy.select { |i| i.even? }.take(10).force
# => [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

[SEE_ALSO] Enumerable#select

filter_map {|item| ... } -> Enumerator::Lazy[permalink][rdoc][edit]

Enumerable#filter_map と同じですが、配列ではなく Enumerator::Lazy を返します。

[EXCEPTION] ArgumentError:
ブロックを指定しなかった場合に発生します。


1.step.lazy.filter_map { |n| n * 2 if n.even? }
# => #<Enumerator::Lazy: #<Enumerator::Lazy: (1.step)>:filter_map>

1.step.lazy.filter_map { |n| n * 2 if n.even? }.take(10).force
# => [4, 8, 12, 16, 20, 24, 28, 32, 36, 40]

[SEE_ALSO] Enumerable#filter_map

force(*args) -> [object][permalink][rdoc][edit]

全ての要素を含む配列を返します。Lazy から実際に値を取り出すのに使います。

Enumerable#to_a のエイリアスです。



1.step.lazy.take(10).force
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

1.step.lazy.take(10).to_a
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
grep(pattern) {|item| ... } -> Enumerator::Lazy[permalink][rdoc][edit]

Enumerable#grep と同じですが、配列ではなくEnumerator::Lazy を返します。



(100..Float::INFINITY).lazy.map(&:to_s).grep(/\A(\d)\1+\z/)
# => #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: 100..Infinity>:map>:grep(/\A(\d)\1+\z/)>
(100..Float::INFINITY).lazy.map(&:to_s).grep(/\A(\d)\1+\z/).take(10).force
# => ["111", "222", "333", "444", "555", "666", "777", "888", "999", "1111"]

[SEE_ALSO] Enumerable#grep, Enumerable#grep_v, Enumerator::Lazy#grep_v

grep_v(pattern) {|item| ... } -> Enumerator::Lazy[permalink][rdoc][edit]

Enumerable#grep_v と同じですが、配列ではなくEnumerator::Lazy を返します。



(100..Float::INFINITY).lazy.map(&:to_s).grep_v(/(\d).*\1/)
# => #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: 100..Infinity>:map>:grep_v(/(\d).*\1/)>
(100..Float::INFINITY).lazy.map(&:to_s).grep_v(/(\d).*\1/).take(15).force
# => ["102", "103", "104", "105", "106", "107", "108", "109", "120", "123", "124", "125", "126", "127", "128"]

[SEE_ALSO] Enumerable#grep_v, Enumerable#grep, Enumerator::Lazy#grep

lazy -> self[permalink][rdoc][edit]

self を返します。



lazy = (100..Float::INFINITY).lazy
p lazy.lazy         # => #<Enumerator::Lazy: 100..Infinity>
p lazy == lazy.lazy # => true
reject {|item| ... } -> Enumerator::Lazy[permalink][rdoc][edit]

Enumerable#reject と同じですが、配列ではなくEnumerator::Lazy を返します。

[EXCEPTION] ArgumentError:
ブロックを指定しなかった場合に発生します。


1.step.lazy.reject { |i| i.even? }
# => #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator: 1:step>>:reject>

1.step.lazy.reject { |i| i.even? }.take(10).force
# => [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

[SEE_ALSO] Enumerable#reject

slice_after(pattern) -> Enumerator::Lazy[permalink][rdoc][edit]
slice_after {|elt| bool } -> Enumerator::Lazy

Enumerable#slice_after と同じですが、配列ではなく Enumerator::Lazy を返します。



1.step.lazy.slice_after { |e| e % 3 == 0 }
# => #<Enumerator::Lazy: #<Enumerator: #<Enumerator::Generator:0x007fd73980e6f8>:each>>

1.step.lazy.slice_after { |e| e % 3 == 0 }.take(5).force
# => [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15]]

[SEE_ALSO] Enumerable#slice_after

slice_before(pattern) -> Enumerator::Lazy[permalink][rdoc][edit]
slice_before {|elt| bool } -> Enumerator::Lazy
slice_before(initial_state) {|elt, state| bool } -> Enumerator::Lazy

Enumerable#slice_before と同じですが、配列ではなく Enumerator::Lazy を返します。



1.step.lazy.slice_before { |e| e.even? }
# => #<Enumerator::Lazy: #<Enumerator: #<Enumerator::Generator:0x00007f9f31844ce8>:each>>

1.step.lazy.slice_before { |e| e % 3 == 0 }.take(5).force
# => [[1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14]]

[SEE_ALSO] Enumerable#slice_before

slice_when {|elt_before, elt_after| bool } -> Enumerator::Lazy[permalink][rdoc][edit]

Enumerable#slice_when と同じですが、配列ではなく Enumerator::Lazy を返します。



1.step.lazy.slice_when { |i, j| (i + j) % 5 == 0 }
# => #<Enumerator::Lazy: #<Enumerator: #<Enumerator::Generator:0x00007fce84118348>:each>>

1.step.lazy.slice_when { |i, j| (i + j) % 5 == 0 }.take(5).force
# => [[1, 2], [3, 4, 5, 6, 7], [8, 9, 10, 11, 12], [13, 14, 15, 16, 17], [18, 19, 20, 21, 22]]

[SEE_ALSO] Enumerable#slice_when

take(n) -> Enumerator::Lazy[permalink][rdoc][edit]

Enumerable#take と同じですが、配列ではなくEnumerator::Lazy を返します。

n が大きな数 (100000とか) の場合に備えて再定義されています。配列が必要な場合は Enumerable#first を使って下さい。

[PARAM] n:
要素数を指定します。
[EXCEPTION] ArgumentError:
n に負の数を指定した場合に発生します。


1.step.lazy.take(5)
# => #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator: 1:step>>:take(5)>

1.step.lazy.take(5).force
# => [1, 2, 3, 4, 5]

[SEE_ALSO] Enumerable#take

take_while -> Enumerator::Lazy[permalink][rdoc][edit]
take_while {|item| ... } -> Enumerator::Lazy

Enumerable#take_while と同じですが、配列ではなくEnumerator::Lazy を返します。



1.step.lazy.zip(('a'..'z').cycle).take_while { |e| e.first < 100_000 }
# => #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator: 1:step>>:zip(#<Enumerator: "a".."z":cycle>)>:take_while>

1.step.lazy.zip(('a'..'z').cycle).take_while { |e| e.first < 100_000 }.force.last(5)
# => [[99995, "y"], [99996, "z"], [99997, "a"], [99998, "b"], [99999, "c"]]

[SEE_ALSO] Enumerable#take_while

uniq -> Enumerator::Lazy[permalink][rdoc][edit]
uniq {|item| ... } -> Enumerator::Lazy

Enumerable#uniq と同じですが、配列ではなく Enumerator::Lazy を返します。

with_index(offset = 0) {|(*args), idx| ... } -> Enumerator::Lazy[permalink][rdoc][edit]
with_index(offset = 0) -> Enumerator::Lazy

生成時のパラメータに従って、要素にインデックスを添えて繰り返します。インデックスは offset から始まります。

ブロックを指定した場合の戻り値は生成時に指定したレシーバ自身です。


("a"..).lazy.with_index(1) { |it, index| puts "#{index}:#{it}" }.take(3).force
# => 1:a
#    2:b
#    3:c

[SEE_ALSO] Enumerator#with_index

zip(*lists) -> Enumerator::Lazy[permalink][rdoc][edit]
zip(*lists) {|v1, v2, ...| ... } -> nil

Enumerable#zip と同じですが、配列ではなくEnumerator::Lazy を返します。

ただし一貫性のため、ブロック付きで呼び出した場合は Enumerable#zip と同じ挙動になります。



1.step.lazy.zip(('a'..'z').cycle)
# => #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator: 1:step>>:zip(#<Enumerator: "a".."z":cycle>)>

1.step.lazy.zip(('a'..'z').cycle).take(30).force.last(6)
# => [[25, "y"], [26, "z"], [27, "a"], [28, "b"], [29, "c"], [30, "d"]]

[SEE_ALSO] Enumerable#zip