2016-11-20 17 views
3

私はクラスのスライドを見直そうとしています。コードは「初期作業」を1回印刷し、その後に「後で作業」を2回(後の作業の繰り返し番号を設定することができます)印刷することになっています。しかし、なぜ私はこのコードがうまくいかず、どうすればコードを修正できますか?今のコードが(ことになっている)、「後仕事」よりもむしろ2の無限ループを生成するためk.timesのループカウンタがスタックであるためruby​​のcallccが無限ループを引き起こしますか?

require 'continuation' 
def work 
    p "early work" 
    here = callcc {|here| here} 
    p "later work" 
    return here 
end 

def rework(k) 
    entry = work 
    k.times do |i| 
    entry.call(entry) 
    end 
end 

rework(2) 
+0

あなたは* *「動作しない」とはどういう意味ですか?あなたのタイトルには無限ループが書いてありますが、どうやってそのマニフェストを見ていますか? – UnholySheep

+0

ええ、コードを実行すると "後の作業"が無限に出力されます。あいまいさを残して申し訳ありません。 –

+0

'entry.call(entry)'だけを 'entry.call()'に変更すると、(実行時エラーで停止しますが)希望の動作が得られます。 – UnholySheep

答えて

2

コードは動作しません。 entry.call(entry)への各呼び出しは、callccが返されたときにプログラムを巻き戻します。したがって、callccが再び戻り、後の作業が再び行われ、workが再び戻り、k.timesが再び開始します。 k.timesが起動すると、ループカウンタはゼロにリセットされます。無限ループは、ループカウンタが常にゼロであるためです。

プログラムを修正するには、ループを再開するのではなく、ループを続行する必要があります。ファイバーを使用するのが最善の解決策ですが、まず、継続を使用しようとします。ここに私のマシン上で動作するバージョンがあります:

require 'continuation' 
def work 
    p "early work" 
    here = callcc {|here| here} 
    p "later work" 
    return here 
end 

class Integer 
    def my_times 
    i = 0 
    while i < self 
     yield i 
     i += 1 
    end 
    end 
end 

def rework(k) 
    entry = nil 
    k.my_times do |i| 
    if i == 0 
     entry = work 
    else 
     entry.call(entry) 
    end 
    end 
end 

rework(2) 

私はループ内workを呼び出すことにより、制御フローを固定します。 workが再び戻ると、私はループカウンタをリセットしません。

私自身もInteger#my_timesを定義しており、RubyのInteger#timesは使用しません。 k.my_timesからk.timesにコードを変更すると、ループカウンタが再び停止します。これにより、Rubyの継続オブジェクトに問題が発生します。

継続がプログラムを巻き戻すと、ローカル変数の値を巻き戻したり保持したりすることがあります。私のプログラムでは、entry.callはループカウンターを保存していると仮定しています。 MatzのRuby実装では、ループカウンタはInteger#my_timesに保存されますが、ループカウンタはInteger#timesに巻き戻されます。これが私のプログラムでInteger#timesを使用できない唯一の理由です。

MRIはローカルコードを(Integer#timesのように)Cコードで巻き戻しているようですが、Rubyコードではローカルに保存しています(Integer#my_timesなど)。これは、ループカウンターや他の地元の人々を混乱させます。 Rubyはこの混乱を修正しませんが、callccに対して警告します。 Rubyは、warning: callcc is obsolete; use Fiber insteadと言っています。

は、ここで繊維を使用したプログラムです:

def work 
    p "early work" 
    here = Fiber.new do 
    while true 
     p "later work" 
     Fiber.yield 
    end 
    end 
    here.resume 
    return here 
end 

def rework(k) 
    entry = nil 
    k.times do |i| 
    if i == 0 
     entry = work 
    else 
     entry.resume 
    end 
    end 
end 

rework(2) 
関連する問題