问题
I'm trying to learn some ruby. Imagine I'm looping and doing a long running process, and in this process I want to get a spinner for as long as necessary.
So I could do:
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"
But I thought it'd be cleaner to do:
def spinChar
a=['|','/','-','\\']
a.cycle{|x| yield x}
end
# ... skip setup a big loop
print spinChar
# ... do next step of process
print "\b"
Of course the spinChar call wants a block. If I give it a block it'll hang indefinitely.
How can I get just the next yeild of this block?
回答1:
Ruby's yield does not work in the way your example would like. But this might be a good place for a closure:
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 practice I think this solution might be too "clever" for its own good, but understanding the idea of Procs could be useful anyhow.
回答2:
I like all these suggestions, but I found the Generator in the standard library, and I think it's more along the lines of what I wanted to do:
spinChar=Generator.new{ |g|
['|','/','-','\\'].cycle{ |x|
g.yield x
}
}
#then
spinChar.next
#will indefinitly output the next character.
Plain array index
increments with modulus on a frozen array seems to be fastest.
Vlad's thread is nifty but not exactly what I wanted. And in spinner class
the one-line increment would be nicer if Ruby supported i++
like GLYPHS[@i++%GLYPHS.length]
Max's spinner closure
with push shift seems a little intensive to me, but the resulting syntax is almost exactly like this Generator. At least I think that's a closure with proc in there.
Chuck's with_spinner
is actually pretty close to what I wanted, but why break if you don't have to with a Generator as above.
Vadim, thanks for pointing out the generator
would be slow.
"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)
回答3:
I think you were on the right track with cycle
. How about something like this:
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
=> "|"
回答4:
I don't think you quite understand what yield
does in Ruby. It doesn't return a value from a block — it passes a value to the block you've passed to the enclosing method.
I think you want something more like this:
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
回答5:
Once upon a time, I wrote an array. But it's not just an array, it's an array that has a pointer, so you can call next foreverrr! http://gist.github.com/55955
Pair this class with a simple iterator or loop and you are golden.
a = Collection.new(:a, :b, :c)
1000.times do |i|
puts a.current
a.next
end
回答6:
Your code is a bit inside-out, if you'll pardon me saying so. :)
Why not:
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
Now, if you want something like:
with_spinner do
# do my big task here
end
...then you'd have to use 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
回答7:
hehe, the answer above mine is all dirty.
a=['|','/','-','\\']
a << a
a.each {|item| puts item}
来源:https://stackoverflow.com/questions/613305/infinite-yields-from-an-iterator