Question about "gets" in ruby [duplicate] - ruby

This question already has answers here:
Ruby: String Comparison Issues
(5 answers)
Closed 3 years ago.
I was wondering why when I'm trying to gets to different inputs that it ignores the second input that I had.
#!/usr/bin/env ruby
#-----Class Definitions----
class Animal
attr_accessor :type, :weight
end
class Dog < Animal
attr_accessor :name
def speak
puts "Woof!"
end
end
#-------------------------------
puts
puts "Hello World!"
puts
new_dog = Dog.new
print "What is the dog's new name? "
name = gets
puts
print "Would you like #{name} to speak? (y or n) "
speak_or_no = gets
while speak_or_no == 'y'
puts
puts new_dog.speak
puts
puts "Would you like #{name} to speak again? (y or n) "
speak_or_no = gets
end
puts
puts "OK..."
gets
As you can see it completely ignored my while statement.
This is a sample output.
Hello World!
What is the dog's new name? bob
Would you like bob
to speak? (y or n) y
OK...

The problem is you are getting a newline character on your input from the user. while they are entering "y" you are actually getting "y\n". You need to chomp the newline off using the "chomp" method on string to get it to work as you intend. something like:
speak_or_no = gets
speak_or_no.chomp!
while speak_or_no == "y"
#.....
end

once you use gets()...
print that string.. using p(str)
usually string will have \n at the end.. chomp! method should be used to remove it...

Related

Sentence rotate program not working ruby

I'm trying to write a program that takes a user's input (sentence), a word the user wants to rotate around, and outputs a rotated sentence around the word that has been chosen by user.
eg. Sentence: This is a book
Word to rotate around: book
Output: book This is a
I can't seem to exit this loop of entering data (the program keeps asking for input, not doing anything more.)
Please help. Here's my code:
class SentenceRotator
def get_sentence
puts "Please enter your sentence: "
sentence = gets.chomp
get_word
end
def get_word
puts "Please enter the word you want to rotate around: "
word = gets.chomp
if converts_sentence_to_array.include?(word)
rotate_sentence_around_word
else
puts "Your word isn't in the sentence. Please enter another word."
word = gets.chomp
end
rotate_sentence_around_word
end
def converts_sentence_to_array()
get_sentence.split(" ")
end
def rotate_sentence_around_word()
new_array = converts_sentence_to_array.each_with_index {|word,index| converts_sentence_to_array.rotate(index)}
new_array
end
end
new_app = SentenceRotator.new
new_app.rotate_sentence_around_word
new_app = SentenceRotator.new
new_app.rotate_sentence_around_word
So, calling methods: rotate_sentence_around_word => converts_sentence_to_array => get_sentence => get_word => converts_sentence_to_array => get_sentence => ...
Try something like this:
class SentenceRotator
def gets_user_data
puts "Please enter your sentence: "
#sentence = get_sentence.split(" ")
puts "Please enter the word you want to rotate around: "
#word = get_word_to_rotate_on
end
def get_sentence
gets.chomp
end
def get_word_to_rotate_on
word = gets.chomp
return word if #sentence.include?(word)
puts "Your word isn't in the sentence. Please enter another word."
get_word_to_rotate_on
end
def rotate_sentence_around_word()
gets_user_data
#sentence.rotate(#sentence.index(#word)).join(' ')
end
end
new_app = SentenceRotator.new
new_app.rotate_sentence_around_word
Here's the logic you want, split up into separate lines:
# input
sentence = 'This is a book'
word = 'book'
# processing
words = sentence.split
pos = words.index(word)
rotated = words.rotate(pos)
back_together = rotated.join(' ')
# output
p back_together
I'd advise you to separate out your processing code as much as possible. Then you can focus on the terminal input and output logic, which is what actually seems to be your issue.
I can't seem to exit this loop of entering data (the program keeps asking for input, not doing anything more.)
It looks like your foremost problem is to take the input from user properly and enter the rotation logic. You could make use of attr_reader to access the input across methods. I have made some changes to your class to accept the input in multiple steps:
class SentenceRotator
attr_reader :sentence, :rotate_on_word
def get_sentence
puts "Please enter your sentence: "
#sentence = gets.chomp
end
def get_word_to_rotate_on
puts "Please enter the word you want to rotate around: "
#rotate_on_word = gets.chomp
unless sentence.include?(rotate_on_word)
puts "Your word isn't in the sentence. Please enter another word."
get_word_to_rotate_on
end
end
def rotate
puts sentence
puts rotate_on_word
puts 'You have all the info. Add the logic to rotate.'
end
end
> new_app = SentenceRotator.new
> new_app.get_sentence
Please enter your sentence:
This is a very funny book
> new_app
=> #<SentenceRotator:0x00007fee44178c40 #sentence="This is a very funny book">
> new_app.get_word_to_rotate_on
Please enter the word you want to rotate around:
a
> new_app
=> #<SentenceRotator:0x00007fee44178c40 #sentence="This is a very funny book", #rotate_on_word="a">
> new_app.rotate
This is a very funny book
a
You have all the info. Add the logic to rotate.

ruby: undefined local variable or method 'car' -- fixing syntax errors

I have a few syntax errors on my program that are really bothering me. I can't seem to figure out how to fix them as I am new to ruby. The first error is on the title, I have a few more I'm sure. The purpose of the program is to create cars with make, model, and year and have user input how many cars they want then display all of them at the end.
Can someone point me to the right direction?
Here is my code:
class Car
def initialize(make, model, year)
#make = make
#model = model
#year = year
end
print "How many cars do you want to create? "
array_of_cars = Array.new
num_cars = gets.to_i
c = car.new
for i in 1..num_cars
end
puts
print "Enter make for car #{i}: "
make = gets.chomp
print "Enter model for car #{i}: "
model = gets.chomp
print "Enter year for car #{i}: "
year = gets.to_i
c.set_make(make)
c.set_model(model)
c.set_year(year)
array_of_cars << c
end
puts
puts "You have the following cars: "
for car in array_of_cars
puts "#{car.get_year} #{car.get_make} #{car.get_model}"
end
In ruby, class names are constants, so should start with a capital letter, as in class Car. When creating a new object of that class, you call new on the class itself. So you would change car.new into Car.new.
You will also need to define your set_* and get_* methods inside the class, but since this is a common pattern, ruby has attr_accessor available. See this answer for a full explanation of attr_accessor.
Consider that your Car does not do anything, it contains only data and has no methods. When this happens, consider making it a Struct instead of a class. A Struct generates a reader and writer method automatically without even specifying attr_reader.
Car = Struct.new(:make, :model, :year)
array_of_cars = Array.new
while true
puts
print "Enter make for car ('x' to exit): "
make = gets.chomp
break if make == 'x'
print "Enter model for car: "
model = gets.chomp
print "Enter year for car: "
year = gets.to_i
array_of_cars << Car.new(make, model, year)
end
puts
puts 'You have the following cars:' # sorted by year for fun
array_of_cars.sort_by{ | car | car.year }.each do | car |
puts "#{car.year} #{car.make} #{car.model}"
end
A few pieces of advice.
Run Ruby with the -w option :
$ ruby -w cars.rb
cars.rb:17: warning: mismatched indentations at 'end' with 'for' at 16
cars.rb:34: warning: mismatched indentations at 'end' with 'class' at 1
cars.rb:41: warning: mismatched indentations at 'end' with 'for' at 39
and eliminate the cause of warnings.
$ ruby -w cars.rb
How many cars do you want to create? 2
cars.rb:2:in `initialize': wrong number of arguments (given 0, expected 3) (ArgumentError)
from cars.rb:13:in `new'
from cars.rb:13:in `<main>'
new calls initialize, so new must have the same number of arguments
as parameters in initialize. Hence a car can be created only after you have asked all the information.
Don't work in the class. As written, your code is executed when Ruby reads
the class definition. For this exercise, you can leave it in the main level outside the class definition, or put it into a method.
for i in 1..num_cars
end
This loop is empty and does nothing. And prefer powerful iterators instead of this C, Perl, Java style (for, while, etc).
I define strings with apostrophes and keep double quotes when interpolation is needed (even if it's a question of nano seconds and personal choice). See here and there.
If you want to be comfortable with Ruby programming, I recommend The Pickaxe.
There are many ways of doing things in Ruby. The following is one solution.
class Car
attr_reader :make, :model, :year
def initialize(make, model, year)
#make = make
#model = model
#year = year
end
def self.make_car # class method (more precisely : singleton method)
print 'How many cars do you want to create? '
array_of_cars = Array.new
num_cars = gets.to_i
num_cars.times do | i |
real_index = i + 1
puts
print "Enter make for car #{real_index}: "
make = gets.chomp
print "Enter model for car #{real_index}: "
model = gets.chomp
print "Enter year for car #{real_index}: "
year = gets.to_i
=begin
c = Car.new(make, model, year)
array_of_cars << c
=end
# some will tell you to avoid unnecessary variables ...
array_of_cars << Car.new(make, model, year)
end
puts
puts 'You have the following cars:' # sorted by year for fun
array_of_cars.sort_by{ | car | car.year }.each do | car |
puts "#{car.year} #{car.make} #{car.model}"
end
end
end # class Car
Car.make_car

Ruby Character Creation for text RPG

First I just wanted to state I am very new to Ruby. I am a hug fan of Dnd and I wanted to create a text adventure game based off of Dnd rules. The issue I am having (I don't even know if it is possible) is that I am creating a character class and I want the variables assigned outside the class. The reason for this is I don't want the player to have to type:
character.new("Havatr", "elf", "tall and lean", "etc")
This is just an experiment before actually creating the file. This is what i have so far:
class Character
attr_reader :name, :race, :description
def initalize (name, race, description)
#name = name
#race = race
#description = description
end
end
def prompt
print "Enter Command >"
end
puts "What is your name?"
prompt; next_move = gets.chomp.downcase
puts "what is your race?"
prompt; next_move = gets.chomp.downcase
puts "What do you look like?"
prompt; next_move = gets.chomp.downcase
player_one = Character.new("#{a}","#{b}","#{c}")
print player_one
If there is a way to do this can I get assistance with it and if there is a better method then what I am trying please let me know. The idea behind this is to try and dump the class into a yaml file to create a character save.
When I run the code this is what it looks like:
What is your name?
Enter Command > Havatr
What is your race?
Enter Command > Elf
What do you look like?
Enter Command > I look like me
C://core_rules0.0.1/Characters.rb:27:in '': undefined local variable or method 'a' for main:Object (NameError)
There are two problems here. The first is that you misspelled initialize. The second is that instead of saving the values entered by the user in a, b, and c you save each in next_move. That's an easy fix:
puts "What is your name?"
prompt; name = gets.chomp.downcase
puts "What is your race?"
prompt; race = gets.chomp.downcase
puts "What do you look like?"
prompt; desc = gets.chomp.downcase
player_one = Character.new(name, race, desc)
print player_one
You'll notice that I did Character.new(name, race, desc) instead of Character.new("#{a}", "#{b}", "#{c}"). First, I used more descriptive names (one-character variable names are almost always a poor choice, except for well-known conventions like i to represent the iteration number in a loop). Second, I did name instead of "#{name}" because the latter doesn't actually do anything. #{...} is string interpolation in Ruby. It's a way to put values into a string, e.g. "Hello #{name}". But when you don't have anything else in the string, as in "#{name}", it doesn't do anything except convert a to a string—a task for which name.to_s is a better solution, and which is unnecessary anyway because we know name is already a string. "#{whatever}" is always unnecessary; whatever.to_s is sometimes necessary.
The other thing you'll notice is that print player_one prints something like:
#<Character:0x007fc23b88bf08>
...which maybe isn't what you expected. That's because Ruby doesn't know how to print your Character object in a human-readable way. You can tell it how by defining a to_s method. For example:
class Character
# ...
def to_s
"#{name} (#{race} - #{desc})"
end
end
For the inputs in your question, this would yield the following:
puts player_one
# => havatr (elf - i look like me)
(It's all lower-case because you called downcase on each input, which may or may not be the behavior you actually want.)
It looks like there is a simple bug here:
class Character
attr_reader :name, :race, :description
def initalize (name, race, description)
#name = name
#race = race
#description = description
end
end
def prompt
print "Enter Command >"
end
puts "What is your name?"
prompt; a = gets.chomp.downcase
puts "what is your race?"
prompt; b = gets.chomp.downcase
puts "What do you look like?"
prompt; c = gets.chomp.downcase
player_one = Character.new("#{a}","#{b}","#{c}")
print player_one
You were setting the results of the prompt to the variable next_move

Catch and throw not working in ruby

I am trying to make a number guessing game in Ruby but the program exits after I type in yes when I want to play again. I tried using the catch and throw but it would not work. Could I please get some help.
Here is my code.
class Game
def Play
catch (:start) do
$a=rand(11)
puts ($a)
until $g==$a
puts "Guess the number between 0-10."
$g=gets.to_i
if $g>$a
puts "The number you guessed is too high."
elsif $g==$a
puts "Correct you won!!!"
puts "Would you like to play again?"
$s=gets()
if $s=="yes"
$c=true
end
if $c==true
throw (:start)
end
elsif $g<$a
puts "The number you guessed is too low."
end
end
end
end
end
Game.new.Play
Edit: Here's my new code after trying suggestions:
class Game
def Play
catch (:start) do
$a=rand(11)
puts ($a)
while $s=="yes"
until $g==$a
puts "Guess the number between 0-10."
$g=gets.chomp.to_i
if $g>$a
puts "The number you guessed is too high."
elsif $g==$a
puts "Correct you won!!!"
puts "Would you like to play again?"
$s=gets.chomp
if $s=="yes"
throw (:start)
end
elsif $g<$a
puts "The number you guessed is too low."
end
end
end
end
end
end
Game.new.Play
Your first problem is here:
$s=gets()
if $s=="yes"
$c=true
end
The gets method will read the next line including the new line character '\n', and you compare it to only "yes":
> gets
=> "yes\n"
The idiomatic way to fix this in Ruby is the chomp method:
> gets.chomp
=> "yes"
That said, your code has two other deficiencies.
You may come from a language such as PHP, Perl, or even just Bash scripting, but Ruby doesn't require the dollar sign before variables. Using a $ gives a variable global scope, which is likely not what you want. In fact, you almost never want a variable to have global scope.
Ruby uses three types of symbol prefixes to indicate scope - # for instance, ## for class, and $ for global. However the most common type of variable is just local which doesn't need any prefix, and what I would suggest for your code.
I have always been told that it is very bad practice to use exceptions for control structure. Your code would be better served with a while/break structure.
When you do gets(), it retrieves the full line with a '\n' in the end. You need to trim the new line character by using:
$g=gets.chomp.to_i
Same for other gets
Based on your updated code (where you fixed the newline problem shown by others), your new problem is that you have wrapped all your game inside while $s=="true". The very first time your code is run, $s is nil (it has never been set), and so you never get to play. If you used local variables instead of global variables (s instead of $s) this would have become more obvious, because the code would not even have run.
Here's one working way that I would re-write your game.
class Game
def play
keep_playing = true
while keep_playing
answer = rand(11) # Make a new answer each time
puts answer if $DEBUG # we don't normally let the user cheat
loop do # keep going until I break from the loop
puts "Guess the number between 0-10."
guess = gets.to_i # no need for chomp here
if guess>answer
puts "The number you guessed is too high."
elsif guess<answer
puts "The number you guessed is too low."
else
puts "Correct you won!!!",
"Would you like to play again?"
keep_playing = gets.chomp.downcase=="yes"
break
end
end
end
end
end
Game.new.play
I know this doesn't really answer your question about why your code isn't working, but after seeing the code you posted I just had to refactor it. Here you go:
class Game
def initialize
#answer = rand(11)
end
def play
loop do
guess = get_guess
display_feedback guess
break if guess == #answer
end
end
def self.play_loop
loop do
Game.new.play
break unless play_again?
end
end
private
def get_guess
puts "Guess the number between 0-10."
return gets.chomp.to_i
end
def display_feedback(guess)
if guess > #answer
puts "The number you guessed is too high."
elsif guess < #answer
puts "The number you guessed is too low."
elsif guess == #answer
puts "Correct you won!!!"
end
end
def self.play_again?
puts "Would you like to play again?"
return gets.chomp == "yes"
end
end
Game.play_loop

what does #{ do in Ruby? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Meaning of #{ } in Ruby?
I know it is used for meta-programming, and I'm having a hard time trying to wrap my mind about what this operator is doing in the following example:
class Class
def attr_accessor_with_history(attr_name)
attr_name = attr_name.to_s # make sure it's a string
attr_reader attr_name
attr_reader attr_name+"_history"
class_eval %Q"
def #{attr_name}=(value)
if !defined? ##{attr_name}_history
##{attr_name}_history = [##{attr_name}]
end
##{attr_name} = value
##{attr_name}_history << value
end
"
end
end
class Foo
attr_accessor_with_history :bar
end
In general terms, #{...} evaluates whatever's inside of it and returns that, converted to a string with to_s. This makes it a lot easier to combine several things in a single string.
A typical example:
"There are #{n} car#{n == 1 ? '' : 's'} in the #{s}"
This is equivalent to:
"There are " + n.to_s + " car" + (n == 1 ? '' : 's').to_s + " in the " + s.to+_s
It's important to remember that the contents of the #{...} interpolation is actually a block of Ruby code and the result of it will be converted to a string before being combined.
That example of meta programming is awfully lazy as instance_variable_get and instance_variable_set could've been used and eval could've been avoided. Most of the time you'll see string interpolation used to create strings, not methods or classes.
There's a more robust formatter with the String#% method:
"There are %d car%s in the %s" % [ n, (n == 1 ? '' : 's'), s ]
This can be used to add a precise number of decimal places, pad strings with spaces, and other useful things.
#{var} does variable substitution in Ruby. For example:
var = "Hello, my name is #{name}"
The code you've posted is generating a string with the code for an accessor method for the attr_name you've passed in.
It's not doing much really :D. All the red text is basically just a string. The "bla #{var} bla" part is just a nicer way of writing "bla " + var + " bla". Try it yourself in irb:
a = 10
puts "The Joker stole #{a} pies."
what it does is called variable interpolation.
name = "Bond"
p "The name is #{name}. James #{name}."
will output,
> "The name is Bond. James Bond."

Resources