2009-03-05 10 views
4

Ich versuche, etwas Rubin zu lernen. Stellen Sie sich vor, ich mache Schleifen und mache einen langen Prozess, und in diesem Prozess möchte ich einen Spinner so lange wie nötig erhalten.Unendliche Erträge von einem Iterator

So konnte ich tun:

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" 

Aber ich dachte, es sauberer sein würde zu tun:

def spinChar 
    a=['|','/','-','\\'] 
    a.cycle{|x| yield x} 
end 
# ... skip setup a big loop 
print spinChar 
# ... do next step of process 
print "\b" 

Natürlich ist der spinChar Anruf einen Block will. Wenn ich es blockiere, wird es auf unbestimmte Zeit hängen bleiben.

Wie kann ich nur das nächste Jahr dieses Blocks bekommen?

Antwort

6

Rubys yield funktioniert nicht so, wie würde Ihr Beispiel mögen. Aber dies könnte ein guter Ort für ein closure sein:

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 der Praxis denke ich, diese Lösung könnte auch „klug“ sein für seine eigenen gut, aber das Verständnis der Idee des Proc s könnte nützlich sein sowieso.

0

Ihr Code ist ein bisschen nach innen, wenn Sie mir verzeihen, so zu sagen. :)

Warum nicht:

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 

Jetzt, wenn Sie so etwas wie wollen:

with_spinner do 
    # do my big task here 
end 

... dann würden Sie zu Verwendung Multi-Threading haben:

def with_spinner 
    t = Thread.new do 
    ['|','/','-','\\'].cycle { |c| print "#{c}\b" ; sleep(1) } 
    end 
    yield 
    Thread.kill(t) # nasty but effective 
end 
4

Ich glaube nicht, dass Sie wh verstehen bei yield tut in Ruby. Es gibt keinen Wert aus einem Block zurück - es übergibt den Wert an den Block, den Sie an die umschließende Methode übergeben haben.

Ich glaube, Sie etwas mehr wie diese wollen:

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

Ich mag all diese Vorschläge, aber ich fand den Generator in der Standard-Bibliothek, und ich denke, es ist mehr entlang der Linien von dem, was ich tun wollte:

auf einem gefrorenen
spinChar=Generator.new{ |g| 
    ['|','/','-','\\'].cycle{ |x| 
    g.yield x 
    } 
} 
#then 
spinChar.next 
#will indefinitly output the next character. 

Plain array index Schritten mit Modul Array scheint am schnellsten zu sein.

Vlads Thread ist geschickt, aber nicht genau das, was ich wollte. Und in spinner class der einzeiligen Zuwachs wäre schöner, wenn Ruby-i++ wie GLYPHS[@i++%GLYPHS.length] unterstützt

Max spinner closure mit Push-Verschiebung scheint ein wenig intensiver zu mir, aber die resultierenden Syntax ist fast genau wie dieser Generator. Zumindest denke ich, das ist ein Abschluss mit proc drin.

Chucks with_spinner ist eigentlich ziemlich nah an dem, was ich wollte, aber warum brechen, wenn Sie nicht mit einem Generator wie oben haben müssen.

Vadim, danke für das Hinweis auf die generator wäre langsam.

"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) 
+0

Generatoren sind schmerzhaft langsam. Ruby 1.9 hat Fasern, die ungefähr gleich schnell sind. – vava

+0

http://StackOverflow.com/a/14894334/1698467 - welcher Name dieser Lösung? 'Spinnerverschluss'? – skywinder

0

Es war einmal, ich schrieb ein Array. Aber es ist nicht nur ein Array, es ist ein Array mit einem Zeiger, so dass Sie als nächstes für immer anrufen können! http://gist.github.com/55955

Koppeln Sie diese Klasse mit einem einfachen Iterator oder einer Schleife und Sie sind golden.

a = Collection.new(:a, :b, :c) 
1000.times do |i| 
    puts a.current 
    a.next 
end 
0

hehe, die Antwort über meiner ist alles schmutzig.

a=['|','/','-','\\'] 
a << a 
a.each {|item| puts item} 
5

Ich denke, Sie waren auf dem richtigen Weg mit cycle. Wie wäre es mit so etwas:

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 
=> "|" 
Verwandte Themen