Ruby递归 - 堆栈级别太深的错误

时间:2014-06-13 19:41:19

标签: ruby recursion

我正在解决“摇滚,纸张,剪刀”游戏。输入数组并递归输出获胜者。这是我的代码:

   class RockPaperScissors

  # Exceptions this class can raise:
  class NoSuchStrategyError < StandardError;  end

  def self.winner(player1, player2)
    strategy = player1[1]+player2[1]
    raise NoSuchStrategyError.new("Strategy must be one of R,P,S") if strategy !~ /(R|P|S){2}/
    strategy  =~ /rs|sp|pr|rr|ss|pp/i ? player1 : player2
  end


  def self.tournament_winner(tournament)
    if tournament.length==2 && tournament.first[0].is_a?(String) 
         winner(tournament[0],tournament[1]) 
    else 
    #keep slice the array in half
        ***winner(tournament_winner(tournament[0,tournament.length/2]), tournament_winner(tournament[tournament.length/2]))***
    end
  end

end
  1. 我的堆栈级别太深,因为该代码以粗体显示。是因为tournament.length正在改变所以我不应该把它放在递归中?有人能详细解释一下这是怎么发生的吗?

  2. 我搜索了答案,有人使用下面的代码并且工作了。我想知道为什么对锦标赛的引用不会导致相同的递归问题。

    winner(tournament_winner(tournament[0]), tournament_winner(tournament[1]))
    
  3. 感谢您的帮助!

    示例输入:

    [
        [
            [ ["Armando", "P"], ["Dave", "S"] ],
            [ ["Richard", "R"],  ["Michael", "S"] ],
        ],
        [
            [ ["Allen", "S"], ["Omer", "P"] ],
            [ ["David E.", "R"], ["Richard X.", "P"] ]
        ]
    ]
    

2 个答案:

答案 0 :(得分:1)

你的代码没有把锦标赛分成两半 - tournament[tournament.length/2]没有返回数组的后半部分 - 它只返回位置tournament.length/2的项目,而是你应该这样做:

winner(tournament_winner(tournament[0...tournament.length/2]), tournament_winner(tournament[tournament.length/2..-1]))

此外,您不会考虑长度为1的数组,从而导致无休止的递归。

以下是您的代码的工作版本:

def tournament_winner(tournament)
  if tournament.length == 1
    tournament_winner(tournament[0])
  elsif tournament.length==2 && tournament.first[0].is_a?(String) 
    winner(tournament[0],tournament[1]) 
  else 
    winner(tournament_winner(tournament[0...tournament.length/2]), 
      tournament_winner(tournament[tournament.length/2..-1]))
  end
end

答案 1 :(得分:0)

有很多更好的处理方式,比杂乱的正则表达式,令人困惑的缩写和字符串遍布整个地方。

例如,您可以使用更面向对象的方法:

module RockPaperScissors
  class NoSuchStrategyError < StandardError; end

  class Move
    include Comparable
    attr_reader :strategy

    def initialize(strategy)
      raise NoSuchStrategyError unless strategies.include?(strategy)
      @strategy = strategy
    end

    def <=>(opposing_move)
      if strengths[strategy] == opposing_move.strategy
        1
      elsif strengths[opposing_move.strategy] == strategy
        -1
      else
        0
      end
    end

  protected

    def strengths
      {
        rock: :scissors,
        scissors: :paper,
        paper: :rock
      }
    end

    def strategies
      strengths.keys
    end
  end

  class Player
    attr_reader :name, :move
    def initialize(name, move)
      @name, @move = name, move
    end
  end

  class Tournament
    def initialize(*players)
      @player_1, @player_2, _ = *players
    end

    def results
      p1move = @player_1.move
      p2move = @player_2.move

      if p1move > p2move
        "#{@player_1.name} wins."
      elsif p2move > p1move
        "#{@player_2.name} wins."
      else
        "Tie."
      end
    end
  end
end

使用示例:

rock = RockPaperScissors::Move.new(:rock)
paper = RockPaperScissors::Move.new(:paper)

player_1 = RockPaperScissors::Player.new('John Smith', rock)
player_2 = RockPaperScissors::Player.new('Corey', paper)

tournament = RockPaperScissors::Tournament.new(player_1, player_2)
tournament.results #=> "Corey wins."