Rendimenti infiniti da un iteratore
Domanda
Sto cercando di imparare un po 'di rubino. Immagina di fare un ciclo continuo e di fare un lungo processo, e in questo processo voglio ottenere un filatore per il tempo necessario.
Quindi potrei fare:
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"
Ma ho pensato che sarebbe stato più pulito fare:
def spinChar
a=['|','/','-','\\']
a.cycle{|x| yield x}
end
# ... skip setup a big loop
print spinChar
# ... do next step of process
print "\b"
Naturalmente la chiamata spinChar vuole un blocco. Se gli do un blocco, si bloccherà indefinitamente.
Come posso ottenere solo il prossimo anno di questo blocco?
Soluzione
Il yield
di Ruby non funziona in come vorrebbe il tuo esempio. Ma questo potrebbe essere un buon posto per una chiusura :
def spinner()
state = ['|','/','-','\\']
return proc { state.push(state.shift)[0] }
end
spin = spinner
# start working
print spin.call
# more work
print spin.call
# etc...
In pratica penso che questa soluzione potrebbe essere troppo "intelligente" per il suo bene, ma comprendendo l'idea di Proc
s potrebbe essere comunque utile.
Altri suggerimenti
Mi piacciono tutti questi suggerimenti, ma ho trovato Generator nella libreria standard e penso che sia più in linea con quello che volevo fare:
spinChar=Generator.new{ |g|
['|','/','-','\\'].cycle{ |x|
g.yield x
}
}
#then
spinChar.next
#will indefinitly output the next character.
Gli incrementi di ?? array array
semplici con modulo su un array congelato sembrano essere i più veloci.
Il thread di Vlad è elegante ma non è esattamente quello che volevo. E nella classe spinner
l'incremento di una riga sarebbe migliore se Ruby supportasse i ++
come GLYPHS [@i ++% GLYPHS.length]
La chiusura dello spinner di Max
con push shift mi sembra un po 'intensa, ma la sintassi risultante è quasi esattamente come questo generatore. Almeno penso che sia una chiusura con proc lì dentro.
Il di_spinner
di Chuck è in realtà abbastanza vicino a quello che volevo, ma perché rompere se non devi avere un generatore come sopra.
Vadim, grazie per aver sottolineato il generatore
sarebbe 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)
Penso che tu fossi sulla buona strada con ciclo
. Che ne dici di qualcosa del genere:
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
=> "|"
Non credo che tu capisca esattamente cosa yield
fa in Ruby. Non restituisce un valore da un blocco & # 8212; passa un valore a il blocco che hai passato al metodo che racchiude.
Penso che tu voglia qualcosa di più simile a questo:
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
C'era una volta, ho scritto un array. Ma non è solo un array, è un array che ha un puntatore, quindi puoi chiamare nextrrr! http://gist.github.com/55955
Associa questa classe a un semplice iteratore o loop e sei d'oro.
a = Collection.new(:a, :b, :c)
1000.times do |i|
puts a.current
a.next
end
Il tuo codice è un po 'rovesciato, se mi perdoni dirlo. :)
Perché 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
Ora , se vuoi qualcosa del tipo:
with_spinner do
# do my big task here
end
... quindi dovresti utilizzare il multi-threading :
def with_spinner
t = Thread.new do
['|','/','-','\\'].cycle { |c| print "#{c}\b" ; sleep(1) }
end
yield
Thread.kill(t) # nasty but effective
end
hehe, la risposta sopra la mia è tutta sporca.
a=['|','/','-','\\']
a << a
a.each {|item| puts item}