Wie Stackgröße für einen Rubin App zu erhöhen. Rekursive App bekommen: Stack-Ebene zu tief (SystemStackError)

StackOverflow https://stackoverflow.com/questions/242617

  •  04-07-2019
  •  | 
  •  

Frage

Posting einen Stapelüberlauf Frage auf stackoverflow.com, wie amüsant: -)

Ich bin einige rekursive Ruby-Code ausgeführt wird und ich erhalte die: "Stack level too deep (SystemStackError)"

(Ich bin ziemlich sicher, dass der Code funktioniert, dass ich nicht in einer unendlichen rekursiven Todesspirale bin, aber das ist nicht der Punkt sowieso)

Gibt es trotzdem die zulässige Stapeltiefe / Größe für meinen Ruby-App zu ändern?

Ich verstehe nicht ganz, wenn dies eine Einschränkung in Ruby ist, da der Fehler sagt „Stack-Ebene“, die mir den Eindruck erweckt, dass Ruby irgendwie zählt ‚Ebene‘ des Stapels, oder wenn es bedeutet einfach, dass der Stapel voll ist.

Ich habe versucht, dieses Programm unter mit dem gleichen Ergebnis sowohl Vista und Ubuntu. Unter Ubuntu habe ich versucht, mit der Stack-Größe zu ändern ‚ulimit es‘ 8192-16000, aber das hat nichts ändern.

Edit: Vielen Dank für das Feedback.
Ich erkennen, dass eine rekursive Funktion vielleicht verwendet, ist nicht der robusteste Weg zu gehen. Aber das ist auch nicht der Punkt. Ich frage mich nur, ob es einen Weg gibt, die Stapelgröße .. Zeitraum zu erhöhen. Und wie ich bereits erwähnt habe ich versuche, läuft ulimit 16000 es, bevor Sie den Ruby-Skript ausgeführt wird .. ohne Verbesserung .. Bin ich mit etwas falsch gemacht?

Edit2: Ich war in der Tat eine unendliche Rekursion in einem Rande Fall des Codes aufweist.
Der abgeschnittene Rubin Stack-Trace, wenn Sie die "Stack level too deep" Fehler bekommen ein wenig irreführend ist.
Wenn ein rekursive Verhalten mit mehreren Funktionen hat, hat man den Eindruck, dass die Zahl der Rekursion ist viel niedriger als es tatsächlich ist. In diesem Beispiel eine Macht Sache, dass es nach etwas mehr als 190 Anrufe abstürzt, aber es ist tatsächlich rund 15000 Anrufe

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

-Andreas

War es hilfreich?

Lösung

Rubin verwendet den C-Stack, so dass Ihre Optionen sind mit ulimit oder Kompilieren Rubin mit einiger Compiler / Linker-Stack-Größe Flagge. Endrekursion wird noch implementiert und Rubys aktuelle Unterstützung für Rekursion wird, ist nicht so groß. So kühl und elegant Rekursion ist, können Sie mit der Sprache der Einschränkungen der Bewältigung zu betrachten und Ihren Code in einer anderen Art und Weise zu schreiben.

Andere Tipps

Diese Frage und die Antworten erscheinen bis jetzt zurück nach Ruby 1.8.x, die den C-Stack verwendet. Ruby 1.9.x und später eine VM verwenden, das seinen eigenen Stapel hat. In Ruby 2.0.0 und höher kann die Größe des VM-Stack über die RUBY_THREAD_VM_STACK_SIZE Umgebungsvariable gesteuert werden.

Wenn Sie sicher sind, dass Sie nicht eine unendliche Rekursion Situation haben dann algorythm ist pobably nicht für Ruby geeignet auszuführen es in einer recirsive Weise. eine algorythm von Rekursion auf andere Art von Stapel Umwandlung ist ziemlich einfach, und ich schlage vor, Sie versuchen, dass. Hier ist, wie Sie es tun können.

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

recursive(starting_params)

verwandeln wird in

stack = [starting_params]
while !stack.empty?
  current_params = stack.delete_at(0)
  if some_conditions(current_params)
    stack << update_params(current_params)
  end
end

Yukihiro Matsumoto schreibt hier

  

Rubin verwendet C-Stack, so dass Sie brauchen, um   verwenden ulimit eine Grenze festlegen auf   Stack-Tiefe.

Denken Sie darüber nach, was mit dem Code vor sich geht. Wie andere Plakate erwähnt haben, ist es möglich, den C-Code des Interpreters zu hacken. Jedoch. das Ergebnis wird sein, dass Sie keine Garantie mehr RAM und haben verwenden, die Sie den Stapel wieder nicht blasen.

Die wirklich gute Lösung wäre, mit einem iterativen Algorithmus kommen für das, was Sie zu tun versuchen. Manchmal kann Memoisation helfen und manchmal feststellen, dass Sie nicht das Zeug mit Ihnen auf den Stapel schieben, in dem Fall, dass Sie rekursive Aufrufe mit wandelbaren Zustand ersetzen.

Wenn Sie auf diese Art von Sachen neu sind, haben einen Blick auf SICP hier für einige Ideen ...

Gerade hatte das gleiche Problem und es ist sehr einfach unter Linux oder auf einem Mac zu beheben. Wie in den anderen Antworten gesagt, verwendet Ruby die System-Stack-Einstellung. Sie können dies leicht auf Mac und Linux ändern, indem Sie die Stapelgröße einstellen. Fox Beispiel:

ulimit -s 20000

Wie von Rubin 1.9.2 können Sie einschalten tail-Call-Optimierung mit so etwas wie:

RubyVM::InstructionSequence.compile_option = {
  tailcall_optimization: true,
  trace_instruction: false
}

RubyVM::InstructionSequence.new(<<-EOF).eval
  def me_myself_and_i
    me_myself_and_i
  end
EOF
me_myself_and_i # Infinite loop, not stack overflow

Das wird die SystemStackError Fehler vermeiden, wenn der rekursive Aufruf am Ende des Verfahrens ist und nur die Methode . Natürlich wird in diesem Beispiel statt in einer Endlosschleife führen. Wahrscheinlich flache Rekursion am besten debuggen (und keine Optimierung), bevor nach einem tiefen Rekursion gehen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top