问题
I'm a novice ruby programmer and although this code works, I'm wondering how I can improve it. I have very limited knowledge on lambdas and procs and the like, but any advice would be great. Is there any way to simplify the if else
statements in each case? Also, is there any alternative way to the case
statement skipped instead of making almost the entirety of the code one big if else statement?
def rps(roll)
roll_ops = ["rock", "paper", "scissors"]
pick = roll_ops.sample
result = nil
if roll == pick
result = "tie"
else
case roll
when "scissors" then
if pick == "paper"
result = "win"
else
result = "lose"
end
when "rock" then
if pick == "scissors"
result = "win"
else
result = "lose"
end
when "paper" then
if pick == "rock"
result = "win"
else
result = "lose"
end
else
puts "Please input rock paper or scissors"
end
end
puts "#{pick}, #{result}"
end
rps("scissors")
回答1:
You can build a hash that contains the pick and which option lose against that pick:
hash = {'scissors' => 'paper', 'rock' => 'scissors', 'paper' => 'rock'}
Then you check if the machine pick is the same like you did:
roll_ops = ["rock", "paper", "scissors"]
pick = roll_ops.sample
if roll == pick
And the win/lose condition becomes something like this:
if hash[roll] == pick
"win"
else
"lose"
end
Nice and clean with just 2 conditions.
回答2:
ROLL_OPS = %w[rock paper scissors]
RESULTS = %w[tie lose win]
def rps(roll)
unless i = ROLL_OPS.index(roll)
return puts "Please input rock paper or scissors".freeze
end
pick = ROLL_OPS.sample
puts "#{pick}, #{RESULTS[(i - ROLL_OPS.index(pick)) % 3]}"
end
回答3:
Here are a couple of ways:
#1 Use Array#cycle
OPS = %w[rock paper scissors]
def rps(roll)
pick = OPS.sample
enum = OPS.cycle
(prev = enum.next) until enum.peek == roll
return [pick, "lose"] if prev == pick
enum.next
return [pick, "win"] if enum.peek == pick
[pick, "tie"]
end
rps "scissors" #=> ["scissors", "tie"]
rps "scissors" #=> ["scissors", "tie"]
rps "scissors" #=> ["rock", "win"]
rps "scissors" #=> ["paper", "lose"]
#2 Take @MurifoX's answer one step farther
def rps(roll)
roll_ops = %w|rock paper scissors|
h = (roll_ops + [roll_ops.first]).each_cons(2).
with_object(Hash.new("tie")) { |(a,b),h| h[[a,b]]="lose"; h[[b,a]]="win" }
pick = roll_ops.sample
[pick, h[[roll,pick]]]
end
rps "scissors" #=> ["rock", "lose"]
rps "scissors" #=> ["scissors", "tie"]
rps "scissors" #=> ["paper", "win"]
Here:
h #=> {["rock", "paper"]=>"lose", ["paper", "rock"]=>"win",
# ["paper", "scissors"]=>"lose", ["scissors", "paper"]=>"win",
# ["scissors", "rock"]=>"lose", ["rock", "scissors"]=>"win"}
and because of the default value "tie":
h[["rock", "rock"]] #=> "tie"
h[["paper", "paper"]] #=> "tie"
h[["scissors", "scissors"]] #=> "tie"
回答4:
I think a proc or other similar setup is probably overkill. Just use inline-ifs:
def rps(roll)
raise "Choose rock, paper, or scissors" if roll.nil?
roll_ops = ["rock", "paper", "scissors"]
pick = roll_ops.sample
result = if roll == pick
"tie"
else
case roll
when "scissors"
pick == "paper" ? 'win' : 'lose'
when "rock"
pick == "scissors" ? 'win' : 'lose'
when "paper" then
pick == "rock" ? 'win' : 'lose'
end
end
puts "#{pick}, #{result}"
end
rps("scissors")
I removed your extra else that was supposed to handle non-input. Better to use errors in such a case.
There are a couple tricks here:
1- The inline-ifs. Those should be pretty clear.
2- The result variable is set equal to the return value of the if expression. This is a handy trick that you can use because in Ruby, everything is an expression!
If you are interested in using a lambda for this, it should work pretty well too:
def rps(roll)
raise "Choose rock, paper, or scissors" if roll.nil?
roll_ops = ["rock", "paper", "scissors"]
pick = roll_ops.sample
did_win = lambda do |choice|
return choice == pick ? 'win' : 'lose'
end
result = if roll == pick
"tie"
else
case roll
when "scissors"
did_win.call('paper')
when "rock"
did_win.call('scissors')
when "paper" then
did_win.call('rock')
end
end
puts "#{pick}, #{result}"
end
来源:https://stackoverflow.com/questions/32105234/drying-up-rock-paper-scissors