#ruby #recursion
#ruby #рекурсия
Вопрос:
Я решаю игру «камень, ножницы, бумага». Введите массив и рекурсивно выведите победителя. Вот мой код:
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 amp;amp; 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
-
Я получил слишком глубокий уровень стека, потому что этот код выделен жирным шрифтом. Это потому, что турнир.длина меняется, поэтому я не должен помещать ее в рекурсию? Может ли кто-нибудь дать подробное объяснение того, как это произошло?
-
Я искал ответ, и кто-то использовал приведенный ниже код и работал. Интересно, почему эта ссылка на турнир не вызовет такой же проблемы с рекурсией.
winner(tournament_winner(tournament[0]), tournament_winner(tournament[1]))
Спасибо за любую помощь!
пример ввода:
[
[
[ ["Armando", "P"], ["Dave", "S"] ],
[ ["Richard", "R"], ["Michael", "S"] ],
],
[
[ ["Allen", "S"], ["Omer", "P"] ],
[ ["David E.", "R"], ["Richard X.", "P"] ]
]
]
Ответ №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 amp;amp; 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
Ответ №2:
Есть так много лучших способов справиться с этим, чем беспорядочные регулярные выражения, запутанные сокращения и строки повсюду.
Например, вы могли бы использовать более объектно-ориентированный подход:
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."