Ruby undefined variable - ruby

I have a code below:
secret_number = 8
user_input = ""
def number_guesser(user_input)
while user_input != secret_number
puts "Guess a number between 1 and 10:"
user_input = gets.chomp
if user_input != secret_number
puts "Wrong! Try again."
else
puts "You guessed correctly!"
end
end
end
number_guesser(user_input)
when I tried to run the above program it showed as below:
****undefined local variable or method secret_number' for main:Object
(repl):211:innumber_guesser'
(repl):221:in `'****
Any ideas?

You can't use a local variable like that inside another scope such as a method, it's two different contexts. Instead you need to pass that in if you want to use it.
It's a simple change:
def number_guesser(user_input, secret_number)
# ...
end
Then just feed that argument in.
You'll note that user_input isn't really necessary as a parameter, you can always initialize and use that locally, so it's actually pointless as an argument.
The pattern to use in that case:
loop do
input = gets.chomp
# Prompting...
break if input == secret_number
# Guessed wrong...
end

Related

I'm trying to design a simple Ruby calculator and I'm getting an error

So I've been messing around with Ruby for the first time after finishing the codecademy course up to "Object Oriented Programming, Part I" and I decided to start making a calculator. For some reason though, I get this error:
calc.rb:13:in `addition': undefined local variable or method `user_input' for main:Object (NameError)
from calc.rb:21:in `<main>'
I'm confused why it doesn't see my "user_input" array. Is it out of the scope of the method? Did I initialize it wrong?
Here's the code so you can see for yourself, it's obviously nothing sophisticated and it's not finished. I'm just trying to test for addition right now.
#!/usr/bin/env ruby
user_input = Array.new
puts "Would you like to [a]dd, [s]ubtract, [m]ultiply, or [d]ivide? "
type_of_math = gets.chomp
def addition
operator = :+
puts "Please enter the numbers you want to add (enter \"=\" to stop adding numbers): "
until gets.chomp == "="
user_input << gets.chomp.to_i
end
sum = user_input.inject(operator)
return sum
end
case type_of_math
when "a"
addition
when "s"
puts "Test for subtraction"
when "m"
puts "Test for multiplication"
when "d"
puts "Test for division"
else
puts "Wrong"
end
Consider this untested variation on your code. It's more idiomatic:
def addition
user_input = []
puts 'Please enter the numbers you want to add (enter "=" to stop adding numbers): '
loop do
input = gets.chomp
break if input == '='
user_input << input
end
user_input.map(&:to_i).inject(:+)
end
Notice that it puts user_input into the method. It also uses the normal [] direct assignment of an empty array to initialize it. Rather than chomp.to_i each value as it's entered it waits to do that until after the loop exits.
Instead of while loops, consider using loop do. They tend to be more easily seen when scanning code.
Also notice there's no return at the end of the method. Ruby automatically returns the last value seen.

How can I make script work in Ruby?

I am new to Ruby.
I need to make this script work:
puts "Do you like cats?"
ask = gets
def ask(n)
if ask == yes
return "I do too"
end
if ask == no
return "Dogs are better"
end
end
puts "#{ask(n)}"
Error message is :
pracif.rb:15:in <main>': undefined local variable or methodn' for
main: Object (NameError)
Here's a script that would work for you :
puts "Do you like cats?"
answer = gets
def ask(n)
if n == 'yes'
return "I do too"
end
if n == 'no'
return "Dogs are better"
end
end
puts ask(answer.downcase.chomp)
Explaination
As the error said you were trying to pass in a variable n which was not defined
Secondly you have a method name ask same as variable name. I've renamed the variable to answer instead
Thirdly, enclose yes and no in quotes
And finally, since you are using gets a \n gets appended like yes\n so none of your conditions would match. So i've used chomp to remove \n. And also used downcase to make input case insensitive.
EDIT
As mentioned by #Jordan in the comments, there is no reason to use string interpolation for the puts statement. So it's enough to call the method directly.
There are a bunch of issues with your code. Try something more like:
def reply(response)
return 'I do too' if response == 'yes'
return 'Dogs are better' if response == 'no'
'Invalid response!'
end
puts 'Do you like cats?'
response = gets().chomp()
puts reply(response)
Pay attention to the variable names. If you keep them descriptive, it is easier to spot mistakes.
Your script has no n local variable defined that you are passing to your ask(n) method at the end.
Rename your ask variable that your script gets from user to answer for example and pass it to your ask method at the end like so:
Updated code to fix other problem I did not see in the first run.
puts "Do you like cats?"
answer = gets.chomp
def ask(n)
(n == 'yes') ? "I do too" : "Dogs are better"
end
puts "#{ask(answer)}"

ruby - syntax error, unexpected tOP_ASGN

The following is a portion of my code. Any advice is appreciated.
def how_many_guesses?
guesses = 0
end
def guess
puts "Please type a number between 1 and a 100."
gets.chomp
end
def correct?
if guess == guess
puts "You have already guessed this number, try again!"
guess
elsif guess == random_num
you_are_correct
elsif guess > correct_num
puts "Too high!"
how_many_guesses? += 1
guess
else
puts "Too low!"
how_many_guesses? += 1
guess
end
end
The error I received was:
syntax error, unexpected tOP_ASGN
how_many_guesses? += 1
I don't understand what unexpected tOP_ASGN means.
As other commented you are trying to assign a value to a function.
You can use some other approaches for example using a class variable, or a setter method.
In the first case you use an instance variable, and you just increment or decrement its value:
class Guess
def initialize
#guesses = 0
end
def guess
puts "Please type a number between 1 and a 100."
gets.chomp
end
def correct?
if guess == guess
puts "You have already guessed this number, try again!"
guess
elsif guess == random_num
you_are_correct
elsif guess > correct_num
puts "Too high!"
#guesses += 1
guess
else
puts "Too low!"
#guesses += 1
guess
end
end
end
In this example you can use attr_reader to publish the guesses variable
The other way, more c++ style, is using getter and setter functions:
class Guess
def initialize
#guesses = 0
end
def get_guesses
#guesses
end
def set_guesses(guesses)
#guesses = guesses
end
def how_many_guesses?
guesses = 0
end
def guess
puts "Please type a number between 1 and a 100."
gets.chomp
end
def correct?
if guess == guess
puts "You have already guessed this number, try again!"
guess
elsif guess == random_num
you_are_correct
elsif guess > correct_num
puts "Too high!"
set_guesses(get_guesses + 1)
guess
else
puts "Too low!"
set_guesses(get_guesses + 1)
guess
end
end
end
This is another solution, but it is not recommended in ruby.
EDIT: Some comments included the option of using global variables, just don't!
To elaborate on #Gavriel's answer, functions are singletons, while += is an assignment operator
Your code can be rewritten to basically look like so:
def how_many_guesses?
guesses = 0
end
how_many_guesses? = how_many_guesses? + 1
Which is impossible, as you can not assign to function symbols in this manner. It is possible to redefine a function on the fly due to Ruby's robust metaprogramming capabilities, but that falls far outside the scope of this question.
The easiest way for you to fix it is to do the following:
numGuesses = how_many_guesses?
numGuesses += 1
how_many_guesses? is a function, you can only assign a value to a variable. But maybe you should read a bit more about functions generally, as your code indicates you might need some clarification.
What you probably wanted to do is something like:
$guesses = 0
and
$guesses += 1
You don't need a function here I think.
But the bigger problem will be that every time you wrote down "guess" it will wait for you to type a number.
if guess == guess # here you'll enter 2 numbers
puts "You have already guessed this number, try again!"
guess # here you'll enter another one
elsif guess == random_num # and if the 1st and 2nd numbers were different then you'll be asked another one here...
Option 1:
Replace all how_many_guesses? with $guesses making it a global variable. Define guesses at the top of the file: $guesses = 0
Option 2 (Better):
Put everything in a class and make guesses an instance variable:
class GuessGame
def initialize
#guesses = 0
end
def guess
# ...
end
def correct?
# ...
#guesses += 1
end
end

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

Memorize gets.chomp

I'm trying to make a text editor in Ruby, but I don't know how to memorize an input with gets.chomp.
Here is my code so far:
outp =
def tor
text = gets.chomp
outp = "#{outp}" += "#{text}"
puts outp
end
while true
tor
end
Ordinary variables , like outp, in a method are only visible (AKA have scope) inside that method.
a = "aaa"
def x
puts a
end
x # =>error: undefined local variable or method `a' for main:Object
Why is that? For one thing, if you are writing a method and you need a counter, you can use a variable named i (or whatever) without worrying about other variables named i outside your method.
But... you want to interact with an outside variable in your method! This is one way:
#outp = "" # note the "", initializing #output to an empty string.
def tor
text = gets.chomp
#outp = #outp + text #not "#{#output}"+"#{text}", come on.
puts #outp
end
while true
tor
end
The # gives this variable a greater visisbility (scope).
This is another way: pass the variable as an argument. It is as saying to your method: "Here, work with this.".
output = ""
def tor(old_text)
old_text + gets.chomp
end
loop do #just another way of saying 'while true'
output = tor(output)
puts output
end

Resources