R: Un iterador es un método que acepta un bloque o un objeto Proc. En un programa fuente, estos aparecen inmediatamente después de la llamada a un método. Se utilizan para crear estructuras de control definidas por los usuarios - especialmente bucles.
Examinemos un ejemplo para ver como funcionan. Los iteradores se utilizan frecuentemente para repetir la misma acción sobre cada el elemento de una colección, como aquí:
data = [1, 2, 3] data.each do |i| print i, "\n" end |
1 2 3 |
Al método each del array data se le pasa el bloque do ... end y se ejecuta repetidamente. En cada llamada al bloque se le pasan sucesivamente los elementos del array.
Se pueden definir los bloques con { .. } en vez de con do ... end.
data = [1, 2, 3]
data.each { |i|
print i, "\n"
}
|
1 2 3 |
Este código es semánticamente igual que el anterior. Sin embargo, en algunos casos, con situaciones de precedencia do .. end y { .. } actúan de forma diferente.
foobar a, b do .. end #foobar es el iterador
foobar a, b { .. } #b es el iterador
|
R: Simplemente situando el bloque después de la llamada al iterador. También se puede pasar un objeto Proc precediendo la variable con & o con un nombre de constante que referencie a un objeto Proc.
R: Existen tres formas de ejecutar un bloque dentro de un método iterador: (1) con la estructura de control yield; (2) utilizando la llamada call del argumento Proc; y (3) utiliando Proc.new seguido de call.
La sentencia yield llama a un bloque, opcionamente le pasa uno o más parámetros.
def miIterador
yield 1, 2
end
miIterador { |a, b| puts a, b }
|
1 2 |
Si la definición de un método tiene un argumento de tipo bloque (el último parámetro formal va precedido de un (&)), recibirá un bloque, convertido a un objeto Proc. Este puede llamarse utilizando method.call(args...).
def miIterador (&b)
b.call(2,3)
end
miIterador{ |a,b| puts a, b }
|
2 3 |
Proc.new (o las llamadas equivalentes proc o lambda) utilizadas dentro de la definición de un iterador, toman el bloque que se le pasa al método como argumento y generan un objeto procedimiento. (proc y lambda son realmente sinónimos).
def miIterador
Proc.new.call(3,4)
proc.call(4,5)
lambda.call(5,6)
end
miIterador {|a,b| puts a, b }
|
3 4 4 5 5 6 |
Sorprendentemente, Proc.new y equivalentes no consumen el bloque añadido al método - cada llamada a Proc.new genera un nuevo objeto procedimiento a partir del mismo bloque.
Se puede comprobar si existe un bloque asociado al método llamando a block_given?.
R: Proc.new sin un bloque no puede generar un objeto procedimiento y da un error. Sin embargo, en la definición de un método si Proc.new no tiene un bloque supone que los tendrá en el momento en que se le llame y no da error.
R: La solución de Matz [ruby-talk:5252] utiliza hilos:
require 'thread'
def combine(*args)
queues = []
threads = []
for it in args
queue = SizeQueue.new(1)
th = Thread.start(it,queue) do |i,q|
self.send(it) do |x|
q.push x
end
end
queues.push queue
threads.push th
end
loop do
ary = []
for q in queues
ary.push q.pop
end
yield ary
for th in threads
return unless th.status
end
end
end
public :combine
def it1 ()
yield 1; yield 2; yield 3
end
def it2 ()
yield 4; yield 5; yield 6
end
combine('it1','it2') do |x|
# x es (1, 2), luego (2, 5), luego (3, 6)
end
|