I am reading the book "Learn to program", and I have come across an exercise that I am having trouble with.
year = (rand(1900...1990))
while true do
puts "What would you like to say to grandma?"
ask = gets.chomp
if
ask == ask.downcase
puts "SPEAK UP SONNY"
elsif
ask == ask.upcase
puts "NO, NOT SINCE #{year}!"
end
break if ask == "BYE"
end
everything is functioning properly, but if the rand number "year" comes up more than once, it repeats the same number. Is there a way to have it become a unique random number each time it comes up during the same loop?
thank you in advance
Move year assignment into the loop to have it assigned before each chomp.
The value of year does not change within the loop because it is defined outside the loop. It would change if the definition were moved inside the loop, but you still may have years repeated. To fix both of these problems, change
year = rand(1900...1990)
to
years = (1900...1990).to_a.shuffle
(outside the loop) and change
puts "NO, NOT SINCE #{year}!"
to
puts "NO, NOT SINCE #{years.shift}!"
(or years.pop). If this is repeated more than 91 times an exception will be raised.
Related
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.
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.
So I've written this piece of code to play around with numbers and the until loop:
number = rand(10)
puts number
puts "Guess the number"
guess = gets.chomp
until guess == number
puts "Guess again!"
guess = gets.chomp
end
puts "You've guessed it right! The number is #{guess}
But for some reason it it always stuck in the until loop and I am not sure why. I puts the random number to know that i guess right and try out the code. I'm completely new to Ruby, So I guess it's a very obvious thing I am missing, but I just dont see it.
From my point of view, whenever I prompt for the guess again, that guess that validated by the until loop with until guess == number.
Who can help me clear this up?
The reason is simple, and that is you're failing to cast the user input into an integer.
i.e. if I write
number = gets.chomp
and I type 1 for the number, then the number variable will equal the string "1", not the integer 1.
To fix this, just use guess = gets.chomp.to_i
Instead of guess = gets.chomp(which will return a string of the user input), use guess = gets.to_i (which will convert user input into integer)
to_i method will convert the element into integer and will drop /n character since it's not part of the integer. Don't need to add .chomp method.
I'm new to ruby and new to stack. I am trying to use the .each method on an array of numbers to see which numbers are divisible by 4, and 400. It is based on an exercises from Chris Pine's "Learn to Program" Ruby tutorial. In it you are supposed to find the leap years, then print them, from a range of years that the user inputs. I accomplished this using an if/else statement...but it seems to me this should be able to be done using the each method, or maybe the map method? Less code.
For example:
puts "Enter two years (to - from) to find out which years are leap years!"
puts "Enter the first year.."
year1 = gets.chomp.to_i
puts "Now enter the second year"
year2 = gets.chomp.to_i
range = (year1..year2).to_a
puts "These are the leap years between those years:"
range.each do |year|
leaps = (year % 4 == 0 || year % 400 == 0)
end
puts leaps
this code may not be correct, but i have toyed with different ways of doing it (puts inside .each, defining variable outside, etc...) but nothing seems to work. Like I said, I accomplished it with an if/else...I just feel there may be a better way, and it's driving me nuts. Do i not understand the .each correctly? am i using the wrong method? can it be done at all using each/map/or collect???? Thanks in advance!
You need to puts inside the each block too. Also, you can just divide by 4 and check if the remainder is 0. No need of 400
range.each do |year|
puts year if year % 4 == 0;
end
The puts year will be executed only if the if condition is satisfied.
I am trying to answer the following question from Chris Pine "Learn to Program" book:
• Write a Deaf Grandma program. Whatever you say to grandma (whatever
you type in), she should respond with HUH?! SPEAK UP, SONNY!, unless
you shout it (type in all capitals). If you shout, she can hear you
(or at least she thinks so) and yells back, NO, NOT SINCE 1938! To
make your program really believable, have grandma shout a different
year each time; maybe any year at random between 1930 and 1950. (This
part is optional, and would be much easier if you read the section on
Ruby's random number generator at the end of the methods chapter.)
You can't stop talking to grandma until you shout BYE. Hint: Don't
forget about chomp! 'BYE'with an Enter is not the same as 'BYE'
without one! Hint 2: Try to think about what parts of your program
should happen over and over again. All of those should be in your
while loop.
• Extend your Deaf Grandma program: What if grandma doesn't want you
to leave? When you shout BYE, she could pretend not to hear you.
Change your previous program so that you have to shout BYE three times
in a row. Make sure to test your program: if you shout BYE three
times, but not in a row, you should still be talking to grandma.
My code is giving me the same "random_year" when I run it. Why is the code not providing me an actual random year (between 1930 and 1950)?
Code in ruby.
# Deaf Grandma
random_year = 1930 + rand(1950 - 1930)
puts 'WHAT DO YOU WANT KID?'
bye = 0
talk_to_grandma = nil
while bye < 3
talk_to_grandma = gets.chomp
if talk_to_grandma == "BYE"
puts 'NO, NOT SINCE ' + random_year.to_s
bye+=1
elsif talk_to_grandma == talk_to_grandma.upcase
puts 'NO, NOT SINCE ' + random_year.to_s
bye = 0
else
puts "HUH?! SPEAK UP, SONNY!"
bye = 0
end
end
You need to generate the random number inside your loop. Right now you generated it ONCE at the start of the program, and then it never changes. Basically:
while bye < 3
random_year = 1930 + rand(1950 - 1930) // move the random generation to here
puts 'NO, NOT SINCE ' + random_year.to_s
You have assigned a value to the random_year variable in the first code line. It is selected randomly.
On each call to
puts 'NO, NOT SINCE ' + random_year.to_s
the variable is accessed/read, but not modified.
You may want to encapsulate the random year generation in a method:
def random_year( start=1930, range = 20 )
start + rand( range )
end
then you can use it like this:
puts "NO, NOT SINCE #{random_year}"
or, for a slightly older grandma:
puts "NO, NOT SINCE #{random_year(1910, 50)}"
For that exercise I've used: rand(1930..1950)