Learn to Program (Chris Pine) Answer explanation - ruby

Chapter 7, first exercise at the end of the chapter (e-book) --> https://pine.fm/LearnToProgram/chap_07.html
I was able to get the code, but only once I set a variable and set it equal to something. I don't really understand why and would appreciate any explanation as to why you have to do this.
Here's my code:
puts "Tell me some of your favorite words-- one at a time, please!"
fave_words = []
word = '0'
while word != ''
word = gets.chomp
fave_words.push word
end
puts "Here are a few of your favorite words..."
puts fave_words.join(' ')
puts "Now in alphabetical order..."
puts fave_words.sort
Thanks in advance!

The condition for the loop to work (and to get into it for the first time mainly) is the word having a value different than an empty string. So it doesn't matter if you make it word = '0' or word ='any other string'. As long as you set it to something different than '' the loop clause will evaluate to true and the loop will start. As long as you provide a non empty input to it it will keep in the loop because the clause will remain evaluating to true.
Actually you don't even need to declare a variable outside of the loop. You could just do something like:
while true
word = gets.chomp
break if word.empty?
fave_words.push word
end
So the loop starts immediately because eval(true) is truth. The loop will keep going until your input is an empty string, condition in which we break the loop.

word is always the input from the user and as long as it's a non-empty string the while loop functions. In order to get the ball rolling word is defined outside of the loop and set to a non-empty string, thus the loop executes.
When the user inputs nothing by pressing enter alone, the while loop condition fails and the loops ceases.

Related

Ruby, `puts` doesn't print anything [duplicate]

This question already has answers here:
Why does Ruby's 'gets' includes the closing newline?
(5 answers)
Closed 2 years ago.
I'm new to writing in Ruby and I have this assignment for my Programming Languages class where I have to implement Mergesort into Ruby such that the user can enter an array of their own choice of numbers and end with a -1. I thought I had everything written in correctly, there are no bugs being reported, but the program doesn't print anything out.
Here's the important part of the code:
puts "Please enter as many numbers as you would like followed by -1"
Many_Numbers = Array.new
x_1 = '-1'
while gets != x_1
Many_Numbers.push gets
end
sorted = merge_sort(Many_Numbers)
puts "SORTED CORRECTLY: #{sorted == Many_Numbers.sort}\n\n#{Many_Numbers}\n\n#{sorted}"
Like I said, nothing is printed out, not even what is provided in the puts methods, so I have nothing to present for an error. What am I doing wrong, here?
EDIT:
I edited the code after I had an idea to improve this part of the code but I still got nothing.
This is what I changed
puts "Please enter as many numbers as you would like followed by -1"
Many_Numbers = Array.new
input = gets
while input != -1
case response
when input != -1
Many_Numbers.push(input)
when input == -1
end
end
You have a couple of problems with your input code.
gets returns all the user input, which includes the newline. So, your loop condition is comparing "-1" to "-1\n" and hence will never end. Calling .chomp on the input will fix that.
You are calling gets twice for each valid number -- once in the loop condition and once when you actually push a value into your array. This causes the loss of one of every two entries. Using a loop do construct with a break condition can fix that problem.
END_OF_LIST = '-1'
puts "Please enter as many numbers as you would like followed by -1"
Many_Numbers = Array.new
loop do
val = gets.chomp
break if val == END_LIST
Many_Numbers.push val
end
The good news is your merge sort method appears to be working once you sort out your input woes.

Simple program but so very stuck- Loops in Ruby

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.

I am unable to break a loop that repeatedly asks for user input. Anyone know how to solve this?

I am trying to create a loop that will ask the user for an integer and will then print "hi" an integer number of times and then ask for input again. I then want the loop to break if the user inputs "bye".
my code so far:
def hi_hi_goodbye
loop do
number = gets
(number.to_i).times do
print "hi"
end
break if
gets == "bye"
end
end
My code so far will not loop. It receives the integer and prints "hi" a given amount of times. However, I am unable to break the loop.
Try
break if gets.chomp == "bye"
This is so because your input would be something like bye\n, so your condition always false, because gets takes even the enter.

Ruby Anagram Comparison Module

I am new to Ruby and trying to figure out a problem for an online test. The problem is as follows:
An anagram is a word formed from another by rearranging its letters,
using all the original letters exactly once; for example, orchestra
can be rearranged into carthorse.
Write a function that checks if two words are each other's anagrams.
For example, AreAnagrams.are_anagrams?('momdad', 'dadmom') should
return true as arguments are anagrams.
The code I have come up with is this:
module AreAnagrams
def self.are_anagrams?(string_a, string_b)
## Create #word1 variable to store string_a
#word1 = string_a
## Create #word1_compare variable to store string_a in lower case
#word1_compare = #word1.downcase
## Create #word2_compare variable to store string_b
#word2 = string_b
## Create #word2_compare variable to store string_b in lower case
#word2_compare = #word2.downcase
## Check length of #word1 and #word2 to make sure they are the same length
if #word1.length == #word2.length
=begin
Sort the letters of the #word1 and #word2 variables and compare
them to see if they are anagrams
=end
if #word1_compare.chars.sort.join == #word2_compare.chars.sort.join
puts "#{#word1} and #{#word2} are anagrams!"
else
puts "#{#word1} and #{#word2} are not anagrams!"
end
else
puts "#{#word1} and #{#word2} are not anagrams!"
end
end
end
When I submit the code, I get the following back from the test:
Compilation OK, but 4 out of 4 test cases fail
Sample case: Wrong answer
Words with unique letters: Wrong answer
Words with duplicate letters: Wrong answer
One word is the prefix of another word: Wrong answer
I have tested my code with multiple strings and it seems to work just fine. It looks like it wants me to check for more specific instances (special characters, words with duplicate letters, etc.) but is that really required? Sorry if it's a dumb question, I'm new to Ruby and lost.
Any help would be greatly appreciated!
I think the problem here is you're displaying a message but not returning a true or false value which is what is expected.
After each puts, include the appropriate answer. That way your method will return something useful. Right now I'm presuming it's nil for all cases, since that's what puts returns.

Need clarifying of exit and abort script Ruby

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.

Resources