2008-10-28 53 views
29

發佈上stackoverflow.com堆棧溢出的問題,:-)如何有趣如何增加一個紅寶石應用程序的堆棧大小。遞歸程序得到:堆棧層次過深(SystemStackError)

我跑了一些遞歸Ruby代碼和我得到的:"Stack level too deep (SystemStackError)"

(我很確定代碼的工作原理,我沒有處於無限的遞歸死亡螺旋,但這不是重點)

是否有更改我的Ruby應用程序允許的堆棧深度/大小?

如果這是Ruby中的限制,我不太明白,因爲錯誤顯示爲「堆棧級別」,這給我的印象是Ruby以某種方式計算堆棧的「級別」,或者它僅僅意味着堆滿了。

我已經嘗試在Vista和Ubuntu下運行該程序的結果相同。 在Ubuntu下,我試圖用'ulimit -s'從8192改爲16000,但這並沒有改變任何東西。

編輯: 感謝您的反饋。
我明白,使用遞歸函數可能不是最強大的方法。 但這也不是重點。 我只是想知道是否有辦法增加堆棧大小..期間。 正如我所說,我曾嘗試在運行ruby腳本之前運行ulimit -s 16000 ..沒有任何改進..我使用它錯了嗎?

編輯2: 我實際上在代碼的邊緣情況下有一個無限遞歸。
當您收到"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 

-Andreas

回答

6

Ruby使用C堆棧,因此您的選項包括使用ulimit或使用某些編譯器/鏈接器堆棧大小標記編譯Ruby。尾遞歸尚未實現,而Ruby對遞歸的支持並不是那麼好。作爲一個很酷且優雅的遞歸,你可能想要考慮應對語言的侷限性,並以不同的方式編寫代碼。

+3

這個答案對於1.9以前的Ruby版本是正確的。對於1.9或更高版本,請參閱http://stackoverflow.com/a/27510458/238886 – 2016-06-12 14:39:06

8

松本行弘寫here

紅寶石採用C堆棧,以便您需要 使用ulimit指定對堆棧深度的限制 。

3

想想代碼正在發生什麼。正如其他海報所提到的,破解解釋器的C代碼是可能的。然而。結果將會是你使用更多的RAM,並且不能保證你不會再次堆棧。

真的很好的解決方案是爲你想要做的事情想出一個迭代算法。有時記憶會有幫助,有時候你會發現你沒有使用你在堆棧中推送的東西,在這種情況下,你可以用可變狀態來替換遞歸調用。

如果你是新來這種東西看看SICP here的一些想法......

13

如果你確信你沒有一個無限遞歸的情況那麼你的algorythm是pobably不適合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 
3

剛剛有同樣的問題,這是非常容易解決在Linux或Mac上。如其他答案中所述,Ruby使用系統堆棧設置。您可以通過設置堆棧大小輕鬆地在Mac和Linux上更改此設置。 Fox示例:

ulimit -s 20000 
11

此問題及其答案似乎回溯到Ruby 1.8.x,它使用了C堆棧。 Ruby 1.9.x和更高版本使用具有自己堆棧的虛擬機。在Ruby 2.0.0及更高版本中,可以通過RUBY_THREAD_VM_STACK_SIZE環境變量來控制VM堆棧的大小。

+4

樂於助人!例如,`export RUBY_T​​HREAD_VM_STACK_SIZE = 5000000`將其設置爲5 MB。 – 2015-12-10 20:11:58

1

由於Ruby 1.9.2你可以打開尾調用優化的東西,如:

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 

這將避免SystemStackError錯誤,如果遞歸調用是在方法的結束和唯一方法。當然,這個例子會導致一個無限循環。在進行深度遞歸之前,最好使用淺遞歸進行調試(並且不進行優化)。