2009-03-05 13 views
4

私はルビーを学ぼうとしています。 私はループしていて長時間実行されているプロセスを実行しているとします。このプロセスでは、必要な時間だけスピナーを取得したいと考えています。イテレータからの無限の歩留まり

だから私は何ができる:

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" 

しかし、私はそれを行うにはきれいだろうと思った。もちろん

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

spinCharコールがブロックを望んでいます。私はそれをブロックすると、無期限にハングアップします。

どうすればこのブロックの次のイールドを得ることができますか?

答えて

6

はRubyのyieldはあなたの例では、希望のように動作しません:

は、私はあなたがより多くのこのような何かをしたいと思います。しかし、これは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... 

が、Proc秒の考えを理解することはとにかく役に立つかもしれません。

0

あなたが私にそう言っているとあなたのコードはちょっと内側です。 :)

なぜ:

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 

、あなたのような何かをしたい場合:

:...

with_spinner do 
    # do my big task here 
end 

を、あなたが使用マルチスレッドする必要があると思います

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

私はあなたがかなり理解しているとは思わないRubyではyieldにあります。ブロックから値を返しません。囲みメソッドに渡したブロックの値はからになります。

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

私はこれらすべての提案を好むが、私は標準ライブラリにジェネレーターを発見した、と私はそれは私がやりたいことの線に沿ってより多くのだと思う:

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

平野、冷凍の弾性率array index増加配列は最速と思われる。

Vladのスレッドは素晴らしいですが、正確には私が望んでいたものではありません。 Rubyはi++をサポートしている場合とspinner classに1行の増分はよりよいだろう

GLYPHS[@i++%GLYPHS.length]のようなMaxの spinner closureプッシュシフトとは、私には少し集中的なようだが、結果の構文はほぼ正確にこの発電機のようなものです。少なくとも私はそれがprocとのクロージャーだと思う。

チャックのwith_spinnerは実際には私が望むものにかなり近いですが、上記のようにジェネレータを使用する必要がない場合はどうして壊れますか?

Vadim、generatorを指摘してくれてありがとうございます。

"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

ジェネレータは痛いほど遅いです。 Ruby 1.9には、ほぼ同じで速くできるFibersがあります。 – vava

+0

http://stackoverflow.com/a/14894334/1698467 - このソリューションの名前は何ですか? 「スピナー閉鎖」? – skywinder

0

いつか、私はアレイを書きました。しかし、それは単なる配列ではなく、ポインタを持つ配列なので、次に呼び出すことができますforeverrr! http://gist.github.com/55955

このクラスと単純なイテレータまたはループをペアにすると、あなたは金色になります。

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

hehe、上記の私の答えはすべて汚いです。

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

あなたはcycleで正しいトラックにいると思います。このようなものはどうですか:

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 
=> "|" 
関連する問題