I am trying to halt input from a user when their input is 42.
The correct answer on the website I'm working on is:
while line = gets
break if (/42/ =~ line)
x << line
end
The code I tried that does not work is:
while line = gets.chomp
break if (line == 42)
x << line
end
Why is this the case? Am I missing some limitations to what I can use in my if statement?
The problem is that 42 is an integer, but line is a string:
1.9.3p392 :001 > "42" == 42
=> false
So it's never the case that your if statement is getting triggered, because it's comparing two different kinds of things. Matching with a Regex fixes it, though it's looking for "42" to appear anywhere in the input (e.g. "3427"). I think what you meant to say was
while line = gets.chomp
break if (line == "42")
x << lineĀ
end
In other words, break when the input is a string with the characters 4 and 2 in it.
I suspect it's because you're comparing a number to a string. The example uses a regular expression it appears. "42" == 42 will give you false in ruby.
<< is a method(Append) on Array or String class objects. But your x not holding any referencing to such objects. Thus you are getting undefined local variable or method 'x' for main:Object (NameError).
Try this instead(by fixing local variable x to hold a practical object and converting line value to Fixnum object:
x = "hi"
while line = gets.chomp
break if (line.to_i == 42)
x << line
end
This program will help you to halt input from a user when their input is 42.
until (line = gets.chomp).to_i == 42
x << line
end
This of course bypasses the if statement you were asking about.
Your limitation for the if is based solely on the fact that you are comparing a string that will always be a string to a number, and this will never be equal. (as others have mentioned)
So we must reconsider the conditional statement. In this case, I considered it "out of place" and moved the comparison to the 'while' loop, and then inverted it to an 'until' statement, to be able to positively express the condition to end the loop on. Whenever I see a 'break' in a loop, I try to get rid of that smell, as the condition to leave a loop should really be expressed in the loop condition if possible.
I hope this helps.
Related
I have to write a program which asks the user to enter a number.
The program keeps on asking the user for a number until the user types 'Stop'
at which point the sum of the numbers that the user has entered should be printed.
I've tried many,many things and none of my ideas work.
This is what I have - but I can that it isn't correct. What am I doing wrong?
I've only used while loops and arrays
total_user_input = []
# As long as the user inputs a number, the program will keep putting Give me a number
# and then adding that number to the total_user_input array.
puts "Give me a number: "
while user_input = gets.chomp.to_i
#add the input to the array total_user_input
total_user_input.push(user_input.to_i)
puts "Give me a number: "
# If the user however types stop, then the loop is broken and we jump down to the
# sum bit - where all of the numbers in the total_user_input array are added together
# and printed. End of program!
if user_input == "stop"
break
end
sum = 0
total_user_input.each { |num|
sum += num
}
puts sum
end
The output isn't as it should be.
As others have identified the problems with your code let me suggest how you might reorganize it. Ruby provides many ways to execute loops but you many find it desirable to primarily relay on the method Kernel#loop and the keyword break. (As you will learn in time, loop is particularly convenient when used with enumerators.)
def sum_numbers
tot = 0
loop do
print 'Gimme a number: '
s = gets.chomp
break if s == 'Stop'
tot += s.to_i
end
tot
end
The keyword break can optionally take an argument (though why that is not mentioned in the doc I cannot say), in which case it (if a literal) or its value (if a variable or method) is returned by loop. Here one would generally see
break tot if s == 'Stop'
without the final line, tot. As the loop returns tot and that is the last calculation performed by the method, the method will return the final value of tot.
You could have instead written
return tot if user_input == 'Stop'
but I think most coders believe best practice dictates that one should not return from a method from within a loop (or from within nested loops) unless there is a good reason for doing so.
Some small points:
I used print rather than puts to that the user's entry will be shown on the same line as the prompt.
I used s (for "string") rather than user_input because it reduces the chance of spelling mistakes (e.g., user_imput), speeds reading, and (possibly a foible of mine), looks neater. True, s is not descriptive, but one only has to remember its meaning for three consecutive lines of code. Others may disagree.
You could write, break if s.downcase == 'stop' if you want, say, 'stop' or 'STOP' to have the same effect as 'Stop'.
'23O3'.to_i #=> 23 (that's an an oh, not a zero), so in real life you'd want to confirm that either 'Stop' or the string representation of a number had been typed.
This is how I would do this preferring to use loop do end syntax with a break when it should. Also added a bit more text so user knows what's happening.
total_user_input = []
puts 'Give me a number or "stop" to end: '
loop do
user_input = gets.chomp
total_user_input << user_input.to_i
puts "Give me a number: "
break if user_input.downcase == "stop"
end
puts "Total entered: #{total_user_input.inject(&:+)}" unless total_user_input.empty?
puts 'goodbye!'
Note these few things:
get.chomp.to_i will convert every input to integer. ("stop" or any non integer string will be 0)
Arrangement of the flow is quite messy.
total_user_input = []
puts "Give me a number: "
while user_input = gets.chomp.strip
total_user_input.push(user_input.to_i)
sum = 0
total_user_input.each { |num|
sum += num
}
puts sum
if user_input == "stop"
break
end
end
Hope you understand this.
Given the following code:
File.open('file1.txt', 'r') do |file|
while line = file.gets
puts "** " + line.chomp.reverse + " **"
end
end
I am confused to what is the question being asked? This is a simple piece of code I got off my tutorial, that reads a file's lines and puts it out. I do understand most of it, I believe you are assigning a variable line to the return value of file.gets, and it retrieves the value of those lines, and puts it out.
Where I am having trouble is the initial loop statement: while line = file gets
My question is that what kind of question are you asking and how does it break out of the loop?
i.e.:x=3 x ==3--> You are asking is X equal to 3, if true will return true, if false will return false.
Also, are you simultaneously assigning the return value of file.gets to the variable line, in addition to putting it in the while statement?
In Ruby everything evaluates to truthy or falsey.
There are two falsey things:
nil
false
Everything else is truthy.
The while loop checks for truthiness of line variable.
Until it is anything but either nil or false it loops.
In your example the loop will stop when file.gets returns nil, meaning, there's no next line.
What happens is that while is using the variable line as its condition. line = file.gets is assigned before while checks the condition. Additionally, while knows how to break out of the loop because at EOF file.gets returns nil which is false-y.
I'm currently trying to create a simply made guessing game, In the code there will be three set number (for now) that a person has to guess. If he/she guesses all the numbers correctly it puts, "Congrats, you win!"
Now as a beginning test i just wanted the user to guess one number correctly and the code gives back correct or incorrect.
random_guess = [1, 3, 5]
puts "Please Pick a number, 1-5"
pick_num = gets.chomp
if pick_num == random_guess = true
puts "Correct!"
else
puts "Incorrect!"
end
(I know this code is very beginner, i'm very new to ruby.) for some reason every time i run this program it puts incorrect.
Your if statement is wrong. It should be:
if random_guess.include? pick_num.to_i
Note that if you leave off the to_i the equality check will always fail because you're comparing the integer 3 against the string "3".
Just to help you a bit more. Since your end goal is to have the user guess all the numbers correctly. You can just loop on the same if statement I wrote above. And every time they guess a correct number you can remove it from the array like such:
random_guess.delete(pick_num.to_i)
Once the array is empty, the user has won.
Making random_guess random:
random_guess = []
3.times{random_guess << rand(1..5)}
random_guess
# => [5, 1, 4] # will be random in every other iteration
Checking if the number exists, you can either use index or include?:
if random_guess.index(pick_num.to_i) # alternatively random_guess.index(pick_num.to_i)
puts "Correct!"
else
puts "Incorrect!"
end
Why is your code always printing incorrect?
You are doing if pick_num == random_guess = true, which is a blunder. What is actually happening here is:
you are assigning true to random_guess. i.e. irrespective to what value (Array) random_guess holds, you are overwriting it with true.
Then you are comparing random_guess with pick_num.
So essentially you are doing this:
if pick_num == (random_guess = true) # say pick_num = "1"
# "1" == true # which is obviously false.
The correct conditional statement should be:
if pick_num == random_guess
However this will also print false every time. Reason?
pick_num is a string.
random_guess is an array that contains integer values.
You are comparing two different object types. So its always false.
Therefore the right way to solve this is checking whether the user entered value exists in the Array. For that you can use Array#index or Array#include?. Hence the statement in my proposed solution:
if random_guess.index(pick_num.to_i)
NOTE: pick_num.to_i converts pick_num (a character) into an integer. This is required as your array contains only integers and not characters.
I am writing a little practice program with if and else. The code is as follows:
puts "What is your name?"
user_name = $stdin.gets.chomp
print "Hello #{user_name}! Welcome to Puzzles and Riddles v.1!"
puts "There are two doors \n 1. Riddles \n 2. Puzzles. \n Which door do you go through?"
answer_1 = $stdin.gets.chomp
if
answer_1 == "1"
puts "You have taken the Riddle room!"
print "Here is your riddle: \n You use a knife to slice my head and weep beside me when I am dead. \n What am I?"
end
riddle_1_answer = $stdin.gets.chomp
if
riddle_1_answer == ( riddle_1_answer == "An onion" ) || ( riddle_1_answer == "an onion" ) || ( riddle_1_answer == "Onion" ) || ( riddle_1_answer == "onion" )
puts "The correct answer is: An onion! \n You have advanced to round two."
else
puts "Sorry, your answer is incorrect. Think about it."
end
puts "Riddle 2. \n What has 4 fingers and a thumb, but is not living?"
riddle_2_answer = $stdin.gets.chomp
Now if the user got riddle_1_answer wrong how would I make it so that the program exits/aborts?
I tried adding exit(0) to the else section and it would terminate the program but would also come up with an error. So I'm not sure if the error is causing the program to end or its the exit(0) command
It's obvious from your question, the sample code, and your answers to #AndrewMarshall, that you need some guidance.
Here you go.
First, ruby is one of many programming languages, and there is a reason why many experienced programmers end up gravitating to it: ruby is object-oriented, expressive, powerful, and reasonably concise without being unnaturally terse. So, if you are going to learn ruby, learn by reading lots of good ruby code, from which you'll learn good practices.
Second, appearance matters because it impacts or enhances readability and comprehension. A good appearance lends to improved readability and more rapid comprehension. A bad appearance does the opposite.
The lack of alignment of the if, else, and end tokens in your code is bad; it made it hard to see the structure of the code's logic.
There are many rules of thumb in programming. Here are a few such rules that apply to most languages:
use alignment and indention properly
always think about "edge cases" (or errors)
limit and isolate complexity (use functions, modules, classes, & methods)
Don't Repeat Yourself (DRY)
So let's apply those two principles to your code and transform it a bit.
The first two lines:
puts "What is your name?"
user_name = $stdin.gets.chomp
What if the user enters CTRL-D (EOF)?
What if the user enters an empty line?
Is an error acceptable? An EOF on STDIN returns a nil, which causes an error on the chomp.
Is an empty string (zero length) name acceptable? If not, what should we do about it?
When there are complexities on doing something relatively simple, like getting a user name, encapsulate the complexities in a function or method, so the code needing the user name is not made confusing by the complexities of getting it.
Here's a replacement. First, let's manage the details (and complexities) of getting a user name within a small function.
def get_user_name
name = ''
while name.size == 0 do
print "What is your name? "
name = gets
exit(1) if name.nil? # exit program on EOF
name.strip!
end
name
end
Notice that we don't use chomp on name until after we've made sure that it isn't nil. Many programs react to EOF on input by exiting, aborting, or continuing on without any more questions. In this example, we'll just assume the user wants to quit.
Notice also that we used strip! instead of chomp!? That's because strip! will remove both leading and trailing whitespace, including the trailing newline.
Notice also that we didn't use $stdin.gets, but instead just gets? This is because the default object for gets is $stdin.
Probably a better approach to managing exceptional situations within small functions (methods), is to raise an exception, and let the higher level application logic decide how to manage it. With that in mind, here is a revised definition:
def get_user_name
name = ''
while name.size < 1 do
print "What is your name? "
name = gets
raise "End of input" if name.nil? # raise exception on EOF
name.strip!
end
name
end
Now, that get_user_name is defined, we can use it wherever we need a user name. We know that EOFs are managed, and we know that we won't get an empty string.
user_name = get_user_name
Now, let's do the rest of your original code, but aligned and indented properly.
print "Hello #{user_name}! Welcome to Puzzles and Riddles v.1!"
puts "There are two doors \n 1. Riddles \n 2. Puzzles. \n Which door do you go through?"
answer_1 = $stdin.gets.chomp
if answer_1 == "1"
puts "You have taken the Riddle room!"
print "Here is your riddle: \n You use a knife to slice my head and weep beside me when I am dead. \n What am I?"
end
riddle_1_answer = $stdin.gets.chomp
if riddle_1_answer == ( riddle_1_answer == "An onion" ) || ( riddle_1_answer == "an onion" ) || ( riddle_1_answer == "Onion" ) || ( riddle_1_answer == "onion" )
puts "The correct answer is: An onion! \n You have advanced to round two."
else
puts "Sorry, your answer is incorrect. Think about it."
end
puts "Riddle 2. \n What has 4 fingers and a thumb, but is not living?"
riddle_2_answer = $stdin.gets.chomp
Now that the alignment and indentation is correct, it's easier to see the logic, and its flaws. It's also easier to see a pattern of logic, and whenever you see a pattern, then DRY it up, and make methods (functions) to encapsulate the repetition.
But first, let's fix the obvious bugs.
The if expression is broken. Look at it again, and you'll see this:
if riddle_1_answer == TEST1 || TEST2 || TEST3 || TEST4
where I've used TESTn to replace the various case-sensitive tests you had.
This if expression will always fail because the value of riddle_1_answer will never be true or false and the result of the various TESTn expressions will always be true or false. I'm pretty sure you wanted this:
if TEST1 || TEST2 || TEST3 || TEST4
Second, when testing for a string value, it's not necessary to test all case variations. Just downcase the answer and test on lowercase test values (unless case-sensitivity is important). Alternatively, if simple character string tests aren't sufficient, then use a regular expression and use the i option for case-insensitive matching. Example:
if riddle_1_answer =~ /(?:an )?onion/i
Will test for "an onion" or "onion" in upper, lower, and mixed case.
Perhaps more important than these little errors, one should look to avoid repetition. The general pattern appears to be:
Ask a question
Accept an answer
Check the answer
Change the program state based on the answer
Repeat
When you see things like this, you should start thinking of arrays and hashes. Arrays are used when the values can be numerically indexed, and hashes are used when you want to get values associated with varying keys. Then, a simple loop can be used to iterate across the values of the array or hash.
So, seeing the pattern above, it becomes more clear that we will need a method to prompt for the question, get the answer, deal with a possible EOF and empty strings, validate non-empty answers, possibly repeating the question & answer when needed.
So let's define a little method to get an answer
# prompt_and_get_answer PROMPT, ANSWERS_DATA
#
# issue PROMPT, and get an answer, which must be one of the
# values in ANSWERS_DATA array, or one of the keys of the
# ANSWERS_DATA hash.
def prompt_and_get_answer prompt, answers_data
ans = ''
while ans.size < 1
print prompt
ans = $stdin.gets
if ans.nil?
raise "End of input"
end
ans.strip!
if answers_data.class == Hash # hash?
answers = answers_data.keys.sort
else
answers = answers_data.sort
end
matches = answers.grep(/#{ans}/i) # match possible valid answers
case matches.size # how many items found?
when 0
puts "#{ans} is not a valid answer. Use one of:"
puts answers.join(', ')
ans = ''
when 1 # return the match or the value of the matching key
ans = answers_data.class == Hash ? answers_data[matches[0]] : matches[0]
else
puts "#{ans} is ambiguous; be more specific to match one of:"
puts answers.join(', ')
ans = ''
end
end
ans
end
Now, we can use this method with a prompt as an argument, and get an answer, knowing that it's not going to be an empty answer and it's not going to be an EOF.
First, let's name the questions, and let's show the prompts as they would appear on output, without using the explicitly escaped newlines (\n).
$prompt1 = <<EOQ
There are two doors
1. Riddles
2. Puzzles
Which door do you go through?
EOQ
$answer1 = [ '1', '2' ]
$prompt2 = <<EOQ
Here is your riddle:
You use a knife to slice my head and weep beside me when I am dead.
What am I?
EOQ
$answer2 = [ 'onion' ]
ans1 = prompt_and_get_answer $prompt1, $answer1
if ans1 == '1'
do_riddles
elsif ans1 == '2'
do_puzzles
else
raise "Bad answer to prompt1"
end
def do_riddles
while true
ans = prompt_and_get_answer $prompt2, $answer2
if ans == 'onion'
puts "yay! you're right!"
break
else
puts "nope. Try again"
end
end
end
You can see from this that we've used methods (functions) to break the logic into smaller pieces, and to separate the details of getting answers from the details of testing them.
Later, we can see how to use tables and hashes to build up a list of questions.
Good luck.
Hi all new to ruby is this if statement valid syntax?
if (verify_login_id = Login.where(params[:email_address], "active" =>1).select('id')# => [#<Login id: 767>]
verify_admin_id = Admin.where("login_id" => verify_login_id.first.id).exists? #=> true)
puts "trueee"
else
raise(ActiveRecord::RecordNotFound.new("Authorization is required to access this endpoint!"))
end
Although setting variable values within an if statement is syntactically valid, it is very problematic.
It can be mistakenly read as a comparison rather than assignment, and in many cases it is a result of someone trying to make a comparison and confusing the equals predicate == with the assignment operator =. That's why a lot of IDEs today mark such code as a warning and a probable error.
In your code it also seems quite unneeded... Break it into two more readable lines:
verified_login = Login.where(params[:email_address], "active" =>1).select('id').first # => [#<Login id: 767>]
if verified_login && Admin.where("login_id" => verified_login.id).exists? #=> true
puts "trueee"
else
raise(ActiveRecord::RecordNotFound.new("Authorization is required to access this endpoint!"))
end
Some more observations - your assignment to verify_login_id is not an id, but a Login object, and your assignment to verify_admin_id is not an id either - but a boolean (and you are not using it anyway). This might seem besides the point - but it adds up to an unreadable and an unmaintainable code.
No it is not a valid code. In the second line, it contains an invalid #-comment that has removed a single ) from the code. (exact snip is #=>true) and should be ) #=>true)
I've removed the comments from the code, added the missing parenthesis and the parser seems to accept it. I couldn't run it of course. So, try this one, it might work:
if (verify_login_id = Login.where(params[:email_address], "active" =>1).select('id')
verify_admin_id = Admin.where("login_id" => verify_login_id.first.id).exists?)
puts "trueee"
else
raise(ActiveRecord::RecordNotFound.new("Authorization is required to access this endpoint!"))
end
Regarding multiline condition in the if - yes, it is possible. Sometimes directly, sometimes with a small trick. Try this:
if (1+2 >=
3)
puts "as"
end
It works (at least on Ruby 1.9.3). However, this will not:
if (1+2
>=
3)
puts "as"
end
This is because of some internal specific of how Ruby interpreter/compiler is designed. In the last example, to make it work, you need to inform Ruby that the line has not ended. The following example works:
if (1+2 \
>=
3)
puts "as"
end
Note the \ added at the end of problematic line