Rendimientos infinitos de un iterador
Pregunta
Estoy tratando de aprender algo de rubí. Imagina que estoy haciendo un bucle y haciendo un proceso de larga duración, y en este proceso quiero obtener una ruleta durante el tiempo que sea necesario.
Entonces podría hacer:
a=['|','/','-','\\']
aNow=0
# ... skip setup a big loop
print a[aNow]
aNow += 1
aNow = 0 if aNow == a.length
# ... do next step of process
print "\b"
Pero pensé que sería más limpio hacer:
def spinChar
a=['|','/','-','\\']
a.cycle{|x| yield x}
end
# ... skip setup a big loop
print spinChar
# ... do next step of process
print "\b"
Por supuesto, la llamada spinChar quiere un bloqueo. Si le doy un bloque, se colgará indefinidamente.
¿Cómo puedo obtener solo el próximo año de este bloque?
Solución
Ruby yield
no funciona en como le gustaría a su ejemplo. Pero este podría ser un buen lugar para un cierre :
def spinner()
state = ['|','/','-','\\']
return proc { state.push(state.shift)[0] }
end
spin = spinner
# start working
print spin.call
# more work
print spin.call
# etc...
En la práctica, creo que esta solución podría ser demasiado inteligente " " por su propio bien, pero entendiendo la idea de Proc
s podría ser útil de todos modos.
Otros consejos
Me gustan todas estas sugerencias, pero encontré el generador en la biblioteca estándar, y creo que es más parecido a lo que quería hacer:
spinChar=Generator.new{ |g|
['|','/','-','\\'].cycle{ |x|
g.yield x
}
}
#then
spinChar.next
#will indefinitly output the next character.
Los incrementos de índice de matriz
con módulo en una matriz congelada parecen ser los más rápidos.
El hilo de Vlad es ingenioso pero no es exactamente lo que quería. Y en class spinner
el incremento de una línea sería mejor si Ruby apoyara i ++
como GLYPHS [@i ++% GLYPHS.length]
cierre de spinner Max
con push shift me parece un poco intenso, pero la sintaxis resultante es casi exactamente como este generador. Al menos creo que eso es un cierre con proc.
El with_spinner
de Chuck en realidad es bastante parecido a lo que quería, pero ¿por qué romper si no tiene que hacerlo con un generador como el anterior?
Vadim, gracias por señalar que el generador
sería lento.
"Here's a test of 50,000 spins:"
user system total real
"array index" 0.050000 0.000000 0.050000 ( 0.055520)
"spinner class" 0.100000 0.010000 0.110000 ( 0.105594)
"spinner closure" 0.080000 0.030000 0.110000 ( 0.116068)
"with_spinner" 0.070000 0.030000 0.100000 ( 0.095915)
"generator" 6.710000 0.320000 7.030000 ( 7.304569)
Creo que estabas en el camino correcto con cycle
. ¿Qué tal algo como esto?
1.8.7 :001 > spinner = ['|','/','-','\\'].cycle
=> #<Enumerable::Enumerator:0x7f111c165790>
1.8.7 :002 > spinner.next
=> "|"
1.8.7 :003 > spinner.next
=> "/"
1.8.7 :004 > spinner.next
=> "-"
1.8.7 :005 > spinner.next
=> "\\"
1.8.7 :006 > spinner.next
=> "|"
No creo que entiendas lo que hace yield
en Ruby. No devuelve un valor de un bloque & # 8212; pasa un valor a el bloque que ha pasado al método de cierre.
Creo que quieres algo más como esto:
def with_spinner
a=['|','/','-','\\']
a.cycle do |x|
print x
$stdout.flush # needed because standard out is usually buffered
yield # this will call the do-block you pass to with_spinner
end
end
with_spinner do
#process here
#break when done
end
Érase una vez, escribí una matriz. Pero no es solo una matriz, es una matriz que tiene un puntero, ¡así que puedes llamar a nextr para siempre! http://gist.github.com/55955
Combina esta clase con un simple iterador o bucle y estarás dorado.
a = Collection.new(:a, :b, :c)
1000.times do |i|
puts a.current
a.next
end
Su código está un poco al revés, si me perdona decirlo. :)
¿Por qué no?
class Spinner
GLYPHS=['|','/','-','\\']
def budge
print "#{GLYPHS[@idx = ((@idx || 0) + 1) % GLYPHS.length]}\b"
end
end
spinner = Spinner.new
spinner.budge
# do something
spinner.budge
spinner.budge
# do something else
spinner.budge
Ahora , si quieres algo como:
with_spinner do
# do my big task here
end
... entonces tendría que usar subprocesos múltiples :
def with_spinner
t = Thread.new do
['|','/','-','\\'].cycle { |c| print "#{c}\b" ; sleep(1) }
end
yield
Thread.kill(t) # nasty but effective
end
jeje, la respuesta sobre la mía está toda sucia.
a=['|','/','-','\\']
a << a
a.each {|item| puts item}