39

stackoverflow.com にスタック オーバーフローの質問を投稿すると、なんと面白いことでしょう :-)

再帰的な Ruby コードをいくつか実行していますが、次の結果が得られます。"Stack level too deep (SystemStackError)"

(私はコードが機能することを確信しています。私は無限の再帰的な死のスパイラルに陥っていませんが、とにかくそれはポイントではありません)

私のRubyアプリの許可されたスタックの深さ/サイズを変更する方法はありますか?

これが Ruby の制限であるかどうかはよくわかりません。エラーに「スタック レベル」と表示されているため、Ruby が何らかの方法でスタックの「レベル」をカウントしているように見えるからです。

このプログラムを Vista と Ubuntu の両方で実行してみましたが、結果は同じでした。Ubuntu では、「ulimit -s」でスタック サイズを 8192 から 16000 に変更しようとしましたが、何も変わりませんでした。

編集:フィードバックをありがとう。
再帰関数を使用することは、おそらく最も堅牢な方法ではないことを認識しています。しかし、それも重要ではありません。スタックサイズを増やす方法があるのだろうか..期間。そして、私が述べたように、ルビースクリプトを実行する前に ulimit -s 16000 を実行しようとしました..改善はありません..私はそれを間違って使用していますか?

Edit2:実際には、コードのエッジケースで無限再帰が発生していました。
エラーが発生したときの切り捨てられた ruby​​ スタック トレース"Stack level too deep"は、少し誤解を招きます。
いくつかの関数を含む再帰動作を行うと、再帰の回数が実際よりもはるかに少ないという印象を受けます。この例では、190 回を少し超える呼び出しの後にクラッシュする可能性がありますが、実際には約 15000 回の呼び出しです。

tst.rb:8:in `p': stack level too deep (SystemStackError)
        from tst.rb:8:in `bar'
        from tst.rb:12:in `bar'
        from tst.rb:19:in `foo'
        from tst.rb:10:in `bar'
        from tst.rb:19:in `foo'
        from tst.rb:10:in `bar'
        from tst.rb:19:in `foo'
        from tst.rb:10:in `bar'
         ... 190 levels...
        from tst.rb:19:in `foo'
        from tst.rb:10:in `bar'
        from tst.rb:19:in `foo'
        from tst.rb:22

-アンドレアス

4

7 に答える 7

27

この質問とその回答は、C スタックを使用していた Ruby 1.8.x にさかのぼるようです。Ruby 1.9.x 以降では、独自のスタックを持つ VM を使用します。RUBY_THREAD_VM_STACK_SIZERuby 2.0.0 以降では、VM スタックのサイズを環境変数で制御できます。

于 2014-12-16T17:29:04.320 に答える
13

無限再帰の状況がないことが確実な場合、そのアルゴリズムは Ruby が再帰的に実行するのに適していない可能性があります。アルゴリズムを再帰から別の種類のスタックに変換するのは非常に簡単です。試してみることをお勧めします。これを行う方法は次のとおりです。

def recursive(params)
  if some_conditions(params)
     recursive(update_params(params))
  end
end

recursive(starting_params)

に変身します

stack = [starting_params]
while !stack.empty?
  current_params = stack.delete_at(0)
  if some_conditions(current_params)
    stack << update_params(current_params)
  end
end
于 2008-10-28T18:38:35.323 に答える
8

松本幸宏はここに書いています

Ruby は C スタックを使用するため、ulimit を使用してスタックの深さの制限を指定する必要があります。

于 2008-10-28T09:32:15.817 に答える
7

Ruby は C スタックを使用するため、ulimit を使用するか、コンパイラ/リンカー スタック サイズ フラグを使用して Ruby をコンパイルするかを選択できます。末尾再帰はまだ実装されておらず、Ruby の現在の再帰サポートはそれほど優れていません。クールで洗練された再帰と同様に、言語の制限に対処し、別の方法でコードを記述することを検討することをお勧めします。

于 2008-10-28T09:35:38.383 に答える
3

同じ問題が発生したばかりで、Linux または Mac で修正するのは非常に簡単です。他の回答で述べたように、Ruby はシステム スタック設定を使用します。これは、Mac と Linux でスタック サイズを設定することで簡単に変更できます。キツネの例:

ulimit -s 20000
于 2011-11-14T09:43:16.310 に答える
3

コードで何が起こっているかを考えてください。他の投稿者が言及しているように、インタープリターの C コードをハッキングすることは可能です。でも。その結果、より多くの RAM を使用することになり、スタックが再び壊れないという保証はありません。

本当に良い解決策は、あなたがやろうとしていることに対して反復アルゴリズムを考え出すことです. メモ化が役立つ場合もあれば、スタックにプッシュしているものを使用していない場合もあります。その場合は、再帰呼び出しを変更可能な状態に置き換えることができます。

この種のものに慣れていない場合は、ここで SICP を見てアイデアを見つけてください...

于 2008-10-28T11:50:53.650 に答える