I'm writing a multiple choice quiz where the user has to choose an answer by entering a letter from a
to d
. Each question prompt looks like this:
What is the last letter in alphabet?
(a) Y
(b) R
(c) Z
(d) Q
If the user enters anything else, I want to show a message and print the whole question again until the input is a
, b
, c
or d
.
Here's what I've tried: (simplified)
question = "What is the last letter in alphabet?\n(a) Y\n(b) R\n(c) Z\n(d) Q"
puts question
answer = gets.chomp.to_s
while answer > "d"
puts "Please enter a, b, c, or d"
puts
puts question
answer = gets.chomp.to_s
end
It works fine when entering e
, f
, g
etc. but it doesn't catch input like ab
, 1
, Z
, or when the user just presses enter.
CodePudding user response:
You have only four allowed inputs. Check for those directly. Even if as inelegantly as if !(answer == "a" || answer == "b" ...
). Anything else will accept some invalid input. In particular, if answer > "d"
allows "ab"
as a valid answer.
CodePudding user response:
Your approach doesn't work because answer > "d"
compares both strings character-wise using their (Unicode) code points. To understand what this means, take a look at the Basic Latin chart: (it's equivalent to an ASCII table)
"d" has a codepoint of U 0064. Any smaller codepoint – i.e every character in the chart before "d" – is regarded smaller. This includes all (regular) digits, all (basic latin) uppercase letters and several symbols:
"0" > "d" #=> false
"Z" > "d" #=> false
You could add a lower bound like answer < "a" || answer > "d"
but this would still allow all strings starting with one of the allowed characters, e.g.:
"apple" < "a" || "apple" > "d" #=> false
To actually limit the answer to the four allowed values, you have to compare the string to each of them. You could combine these comparisons:
answer == 'a' || answer == 'b' || answer == 'c' || answer == 'd'
use a loop over an array of allowed values:
['a', 'b', 'c', 'd'].any? { |letter| answer == letter }
check whether the array includes the answer:
['a', 'b', 'c', 'd'].include?(answer)
# or
%w[a b c d].include?(answer)
or use a regular expression to match a
to d
:
answer.match?(/\A[a-d]\z/)
Note that the examples above become true if answer
is between a
and d
. You could either negate the condition via !(...)
:
while !(%w[a b c d].include?(answer))
# ...
end
or use until
instead of while
:
until %w[a b c d].include?(answer)
# ...
end