Des rendements infinis à partir d'un itérateur
Question
J'essaie d'apprendre du rubis. Imaginez que je tourne en boucle et que je travaille sur un long processus, et que je souhaite obtenir un spinner aussi longtemps que nécessaire.
Je pourrais donc faire:
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"
Mais je pensais que ce serait plus propre de faire:
def spinChar
a=['|','/','-','\\']
a.cycle{|x| yield x}
end
# ... skip setup a big loop
print spinChar
# ... do next step of process
print "\b"
Bien sûr, l'appel de spinChar veut un bloc. Si je le bloque, il sera suspendu indéfiniment.
Comment puis-je obtenir la prochaine année de ce bloc?
La solution
rendement
de Ruby ne fonctionne pas. la façon dont votre exemple aimerait. Mais cela pourrait être un bon endroit pour une clôture :
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 pratique, je pense que cette solution est peut-être trop "intelligente". pour son propre bien, mais en comprenant l'idée de Proc
s pourrait être utile de toute façon.
Autres conseils
J'aime toutes ces suggestions, mais j'ai trouvé le générateur dans la bibliothèque standard, et je pense qu'il va plus dans le sens de ce que je voulais faire:
spinChar=Generator.new{ |g|
['|','/','-','\\'].cycle{ |x|
g.yield x
}
}
#then
spinChar.next
#will indefinitly output the next character.
Un index de tableau simple
semble être le plus rapide avec un module sur un tableau gelé.
Le fil de Vlad est chouette mais pas exactement ce que je voulais. Et dans spinner class
, l'incrément d'une ligne serait plus agréable si Ruby supportait i ++
comme GLYPHS [@i ++% GLYPHS.length]
La fermeture de spinner
de Max avec push-shift me semble un peu intensive, mais la syntaxe obtenue est presque identique à celle de ce générateur. Au moins, je pense qu’il s’agit d’une fermeture avec proc.
Le with_spinner
de Chuck est en fait assez proche de ce que je voulais, mais pourquoi rompre si vous n'avez pas à utiliser un générateur comme ci-dessus.
Vadim, merci d'avoir signalé que le générateur
serait lent.
"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)
Je pense que vous êtes sur la bonne voie avec cycle
. Que diriez-vous de quelque chose comme ça:
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
=> "|"
Je ne pense pas que vous compreniez bien ce que cède
fait dans Ruby. Il ne renvoie pas de valeur d'un bloc & # 8212; il transmet une valeur à le bloc que vous avez passé à la méthode englobante.
Je pense que vous voulez quelque chose de plus semblable à ceci:
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
Il était une fois, j’ai écrit un tableau. Mais ce n'est pas simplement un tableau, c'est un tableau qui a un pointeur, vous pouvez donc appeler ensuite foreverrr! http://gist.github.com/55955
Associez cette classe à un simple itérateur ou à une boucle et vous êtes dorés.
a = Collection.new(:a, :b, :c)
1000.times do |i|
puts a.current
a.next
end
Votre code est un peu à l'envers, si vous me permettez de le dire. :)
Pourquoi pas:
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
Maintenant , si vous voulez quelque chose comme:
with_spinner do
# do my big task here
end
... vous devez utiliser le 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
héhé, la réponse ci-dessus la mienne est tout sale.
a=['|','/','-','\\']
a << a
a.each {|item| puts item}