La clase Enumerator permite la iteración tanto interna como externa.
next
element explicitly from the iterator.
Una manera de crear objetos Enumerator es mediante el método
to_enum (en la clase Object):
[1] pry(main)> a = [1, 2, "cat" ] => [1, 2, "cat"] [3] pry(main)> ea = a.to_enum => #<Enumerator: ...> [4] pry(main)> ea.next => 1 [5] pry(main)> ea.next => 2 [6] pry(main)> ea.next => "cat" [7] pry(main)> ea.next StopIteration: iteration reached an end from (pry):7:in `next' [8] pry(main)> ea.rewind => #<Enumerator: ...> [9] pry(main)> ea.next => 1 [10] pry(main)> ea.next => 2 [11] pry(main)> ea.peek => "cat"
También puede usarse con hashes:
28] pry(main)> h = { a: "hello", b: "world" }
=> {:a=>"hello", :b=>"world"}
[29] pry(main)>
[30] pry(main)> eh = h.to_enum
=> #<Enumerator: ...>
[31] pry(main)> eh.size
=> nil
[32] pry(main)> eh.next
=> [:a, "hello"]
[33] pry(main)> eh.next_values
=> [[:b, "world"]]
[34] pry(main)> eh.rewind
=> #<Enumerator: ...>
El método to_enum está definido en la clase Object.
The call obj.to_enum(method = :each, *args)
creates a new Enumerator which will enumerate by calling method :each
on obj, passing args
if any.
[2] pry(main)> class Tutu
[2] pry(main)* def chuchu(x)
[2] pry(main)* if block_given?
[2] pry(main)* yield x+1
[2] pry(main)* yield x+2
[2] pry(main)* yield x+3
[2] pry(main)* else
[2] pry(main)* to_enum(:chuchu, x) { 3 }
[2] pry(main)* end
[2] pry(main)* end
[2] pry(main)* end
=> :chuchu
[3] pry(main)> t = Tutu.new
=> #<Tutu:0x007f9e6cb5af60>
[5] pry(main)> t.chuchu(10) { |x| puts x }
11
12
13
=> nil
[6] pry(main)> e = t.chuchu(10)
=> #<Enumerator: ...>
[7] pry(main)> e.next
=> 11
[8] pry(main)> e.next
=> 12
[9] pry(main)> e.size
=> 3
with_index(offset = 0) {|(*args), idx| ... }
(o bien with_index(offset = 0))
iterates the given block for each element with an index, which starts from offset.
If no block is given, returns a new Enumerator
that includes the index, starting from offset
[40] pry(main)> h = { a: "hello", b: "world" }
=> {:a=>"hello", :b=>"world"}
[41] pry(main)> eh = h.to_enum
=> #<Enumerator: ...>
[42] pry(main)> eh.with_index.next
=> [[:a, "hello"], 0]
[44] pry(main)> eh.with_index { |x, i| puts "#{x.inspect}, #{i}" }
[:a, "hello"], 0
[:b, "world"], 1
=> {:a=>"hello", :b=>"world"}
yields a value to your code
next value, execution resumes at the statement following the yields
[~/ruby/PROGRAMMINGRUBYDAVETHOMAS]$ cat fib_enum.rb
@fib = Enumerator.new do |y|
a, b = 0, 1
loop do
a, b = b, a + b
y.yield a
end
end
Enumerator.new recieves a block that is used to define the iteration.
A
yielder object y is given to the block as a parameter
and can be used to yield a value by calling the yield method
(aliased as <<).
[~/ruby/PROGRAMMINGRUBYDAVETHOMAS]$ pry [1] pry(main)> require './fib_enum' => true [2] pry(main)> @fib.next => 1 [3] pry(main)> @fib.next => 1 [4] pry(main)> @fib.next => 2 [6] pry(main)> @fib.first => 1 [7] pry(main)> @fib.take(5) => [1, 1, 2, 3, 5]
La clase Enumerator incluye el módulo Enumerable de donde toma,
entre otros, los métodos first y take.
[25] pry(main)> Enumerator.ancestors => [Enumerator, Enumerable, Object, PP::ObjectMixin, Kernel, BasicObject]
count and select will happily try to read the whole enumeration
before returning a result
select that
works with infinite sequences, you’ll need to write it yourself
true
[1] pry(main)> require './fib_enum'
=> true
[2] pry(main)> require './grep8'
=> true
[3] pry(main)> e = @fib.grep8 { |x| x % 3 == 0}
=> #<Enumerator: ...>
[4] pry(main)> e.next
=> 3
[5] pry(main)> e.next
=> 21
[6] pry(main)> e.next
=> 144
[7] pry(main)> e.first(5)
=> [3, 21, 144, 987, 6765]
[9] pry(main)> .cat grep8.rb
class Enumerator
def grep8(&block)
Enumerator.new do |y|
self.each do |x|
y.yield x if block[x]
end
end
end
end
[16] pry(main)> e = (1..Float::INFINITY).lazy.map { |x| x*x }
=> #<Enumerator::Lazy: ...>
[17] pry(main)> e.next
=> 1
[18] pry(main)> e.next
=> 4
[19] pry(main)> e.next
=> 9
[20] pry(main)> e.next
=> 16
[21] pry(main)> e.take(4).force
=> [1, 4, 9, 16]
[22] pry(main)> e.take(4)
=> #<Enumerator::Lazy: ...>
[23] pry(main)> e.first(4)
=> [1, 4, 9, 16]
En este otro ejemplo
creamos un enumerador perezoso filter_map
que devuelve los elementos procesados por el bloque cuyo valor
no es nil. Su funcionamiento sería así::
[1] pry(main)> require './lazy_enum'
=> true
[2] pry(main)> [1,2,3].map {|x| x*x if x % 2 != 0 }
=> [1, nil, 9]
[3] pry(main)> [1,2,3].filter_map {|x| x*x if x % 2 != 0 }
=> [1, 9] # se elimina el nil
Para usarlo con rangos infinitos:
[4] pry(main)> (1..Float::INFINITY).lazy.filter_map{|i| i*i if i.even?}.first(5)
=> [4, 16, 36, 64, 100]
El método lazy se encuentra en el módulo Enumerable
y retorna un enumerador perezoso Enumerator::Lazy
que enumera/construye el valor cuando se
necesita.
[~/ruby/PROGRAMMINGRUBYDAVETHOMAS]$ cat lazy_enum.rb
module Enumerable
def filter_map(&block) # Para explicar la semantica
map(&block).compact
end
end
class Enumerator::Lazy
def filter_map
Lazy.new(self) do |yielder, *values|
result = yield *values
yielder << result if result # << es un alias de yield
end
end
end
The call new(self) { |yielder, *values| ... }
creates a new Lazy enumerator using self as the coupled enumerator.
When the enumerator is actually enumerated (e.g. by calling force or
next),
self will be enumerated and each value passed to the given block.
The block then yield values back using yielder.yield or yielder << ... .
[~/rubytesting]$ cat primes.rb
def primes
(1..Float::INFINITY).lazy.select do |n|
((2..n**0.5)).all? { |f| n % f > 0 }
end
end
p primes.first(10)
[~/rubytesting]$ ruby primes.rb
[1, 2, 3, 5, 7, 11, 13, 17, 19, 23]
Casiano Rodriguez León 2015-01-07