2012-01-04 3 views
5

루비의 재귀 함수에서 스택 오버플로 오류에 대한 해결 방법이 있습니까? 재귀 루틴의 "stack level too deep"오류에 대한 해결책이 있습니까?

def countUpTo(current, final) 
    puts current 
    return nil if current == final 
    countUpTo(current+1, final) 
end 

내가 countUpTo(1, 10000)를 호출하는 경우

, 나는 오류가 발생 : stack level too deep (SystemStackError)

는 말, 예를 들어,이 블록을 가지고있다.

8187에서 멈추는 것처럼 보입니다. 루비가 스택의 크기를 무시하도록하거나 최대 스택 크기를 늘리는 방법이 있습니까?

당신은 아닌 조각을 다시 작성할 수 있습니다
+5

이렇게하지 마십시오. 당신이 의도적으로 10,000 번을 recusing한다면, 당신은 잘못하고 재귀를 악용하고 있습니다. – meagar

+2

Ruby 구현은 반드시 꼬리 호출 제거를 수행하지 않으므로 C 스택 크기 사용에 의존합니다. 한 가지 가능성은 반복적으로 함수를 다시 작성할 수 있다는 것입니다. – birryree

+0

첫째, Ruby에 대한 저의 경험은 재귀에서 특히 좋지 않습니다.이 오류는 매우 쉽게 발생하고 느리다는 것입니다. 또한,이 영역에서 더 나은 성능을 얻으려면 Ruby를 특정 상수 세트로 컴파일해야하지만,별로 도움이되지 않았습니다. 즉,'times','upto'와 같은 일반적인 Ruby 메소드를 사용하여 함수를 다르게 작성하십시오. 목표가 무엇인지 알지 못한다면, 당신이 그 주장을 할 수 있다고 생각하지 않습니다. 나는 하스켈에서 몇 번이나 문제없이 반복적으로 반복하는 방법을 썼다. – iain

답변

2

당신이, 당신이 꼬리를 호출 최적화를 설정하는 루비 VM을 알 수 있습니다 (루비 1.9의 C 기반 구현) YARV를 사용하는 경우 :

루비 2.0 당신이 할 수에서
RubyVM::InstructionSequence.compile_option = { 
    :tailcall_optimization => true, 
    :trace_instruction => false 
} 

def countUpTo(current, final) 
    puts current 
    return nil if current == final 
    countUpTo(current+1, final) 
end 

countUpTo(1, 10_000) 
+0

내가 위에서 쓴 것처럼, 나는 이것이 작은 영향을 끼친다는 것을 알았지 만, YMMV. – iain

+0

compile_option을 설정 한 후, 명령어 순서에'new' 또는'compile' 메소드를 사용할 수 있습니다. 예 :'RubyVM :: InstructionSequence.new ('def meth_name (b, c); d = b + c; puts " # {b + 1} + # {c} ... "; meth_name (b + 1, c) end ') .eval'. # {b} + # {c} 더 일반적인 방법은 [여기] (https://gist.github.com/rogerleite/5101751)입니다. –

+0

또는 모든 옵션을 설정하지 않으려면 옵션을 compile/new에 전달할 수 있습니다 (예 : # {b} + # {c} = # {d}을 (를) 지금 # {b + 1} + # {c} ... "; meth_name (b + 1, c) end ', nil, nil, nil, tailcall_optimization : true, trace_instruction : false) .eval'. 자세한 내용은 [:: RubyVM :: InstructionSequence] (http://www.ruby-doc.org/core-2.0.0/RubyVM/InstructionSequence.html)를 참조하십시오. –

2

는 재귀합니다 :

# 'count_up_to' would be a more "Ruby" name ;-) 
def countUpTo(current, final) 
    (current..final).each { |i| puts i } 
end 

난 당신의 코드는 아마 당신이 정말로 뭘 하려는지의 추상화 감사하지만, 당신이 생각하는 경우 용액을 형성 도움이 될 수 있습니다 재귀 적으로 반복하는 것이 아니라 반복하는 다른 방법이 있습니다.

HTH

+0

일반적으로 제한되지 않은 재귀는 잘못되었습니다. 대부분의 재귀는 Pavling이 보여준 것처럼 반복적으로 재 작성 될 수 있습니다. – nmjohn

+0

이것은 어떻게 downvote의 가치가 있었습니까?! } : - [ – Pavling

+0

@ Pavling : 당신이 말하는 이유는 그것을하지 않을 정당한 이유를 제시하지 않고 "하지 마라"(반복하지 마라). –