Como aumentar o tamanho da pilha para um aplicativo de rubi. aplicação recursiva recebendo: nível de pilha muito profundo (SystemStackError)

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

  •  04-07-2019
  •  | 
  •  

Pergunta

postar uma pergunta estouro de pilha no stackoverflow.com, como divertindo: -)

Eu estou correndo algum código Ruby recursiva e tenho a: "Stack level too deep (SystemStackError)"

(tenho certeza o código funciona, que eu não estou em uma espiral da morte recursiva infinita, mas isso não é o ponto de qualquer maneira)

Existe uma maneira de mudar o permitido profundidade da pilha / tamanho para meu aplicativo Ruby?

Eu não bastante obtê-lo se esta é uma restrição em Ruby, uma vez que o erro diz "nível de pilha", o que me dá a impressão de que Ruby alguma forma contagens 'níveis' de pilha, ou se ele simplesmente significa que a pilha está cheio.

Eu tentei executar este programa sob Vista e Ubuntu com o mesmo resultado. Sob Ubuntu eu tentei mudar o tamanho da pilha com 'ulimit -s' 8192-16000, mas isso não muda nada.

Edit: Obrigado pelo feedback.
Eu reconheço que o uso de uma função recursiva, talvez, não é a forma mais robusta para ir. Mas isso não é o ponto de qualquer um. Eu simplesmente de saber se existe uma maneira de aumentar o tamanho da pilha .. período. E como mencionei eu fiz tente executar ulimit -s 16000 antes de executar o script Ruby .. sem melhora .. Am I usá-lo errado?

Edit2: Eu estava, de facto, ter uma recursividade infinita em um caso de limite do código.
O rubi pilha traço truncados quando você obter o erro "Stack level too deep" é um pouco enganador.
Ao ter um comportamento recursiva envolvendo várias funções, você tem a impressão de que os números de recursividade é muito menor do que realmente é. Neste exemplo uma coisa força para que ele trava depois de pouco mais de 190 chamadas, mas é, na verdade, cerca de 15000 chamadas

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

Foi útil?

Solução

Ruby usa a pilha C assim que suas opções incluem o uso de ulimit ou compilar rubi com algum compilador / vinculador tamanho da pilha bandeira. recursão de cauda ainda está para ser implementado e suporte atual do Ruby para a recursividade não é tão grande. Como fresco e recursão elegante é, você pode querer considerar lidar com as limitações da linguagem e escrever seu código de uma maneira diferente.

Outras dicas

Esta questão e suas respostas parecem datam de rubi 1.8.x, que usou a pilha C. Ruby 1.9.x e, posteriormente, usar uma máquina virtual que tem sua própria pilha. Em Ruby 2.0.0 e posteriores, o tamanho da pilha VM pode ser controlado através da variável de ambiente RUBY_THREAD_VM_STACK_SIZE.

Se você tem certeza que você não tem uma situação de recursão infinita então seu algorythm pobably não é adequado para Ruby para executá-lo de uma forma recirsive. Convertendo um algorythm de recursão para diferentes tipos de pilha é muito fácil e eu sugiro que você tentar isso. Aqui está como você pode fazê-lo.

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

recursive(starting_params)

vai se transformar em

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 escreve aqui

Ruby usa pilha C, de modo que você precisa usar ulimit para especificar um limite de pilha de profundidade.

Pense sobre o que está acontecendo com o código. Como outras pôsteres têm mencionados é possível cortar o código C do intérprete. Contudo. o resultado será que você está usando mais memória RAM e não têm garantia de que você não vai explodir a pilha novamente.

O realmente boa solução seria a de chegar a um algoritmo iterativo para o que você está tentando fazer. Às vezes memoisation pode ajudar e às vezes você achar que você não está usando o material que você está empurrando na pilha nesse caso, você pode substituir chamadas recursivas com o estado mutável.

Se você é novo para este tipo de coisas ter um olhar para SICP aqui para algumas idéias ...

Apenas teve o mesmo problema e é muito fácil de correção em Linux ou Mac. Como disse em outras respostas, Ruby usa a configuração de pilha do sistema. Você pode facilmente mudar isso no Mac e Linux, definindo o tamanho da pilha. exemplo Fox:

ulimit -s 20000

A partir de Rubi 1.9.2 você pode ativar a otimização de chamada com algo como:

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

Isso vai evitar o erro SystemStackError se a chamada recursiva é no final do método e apenas o método . Naturalmente, este exemplo vai resultar em um loop infinito em seu lugar. Provavelmente melhor para depuração usando recursão rasa (e não optimization) antes de ir atrás recursão profunda.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top