new to Ruby, if else statement not working [closed] - ruby

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
Well when Input a number, it doesn't print out any of the strings for the given value. if i put in 100, shouldn't it print "go to server room"?
stress = gets.chomp!
def stress
if stress === 100
p "Go to server room"
elsif stress > 50
puts "Take a break"
else
p "Continue working"
end
end
p stress

if i put in 100, shouldn't it print "go to server room"?
I assume that you want your program to read user input and then print different messages depending on that input.
[I'm] new to Ruby
Okay, so first of all, Ruby doesn't do implicit type conversion like JavaScript or PHP. Or not to the same extent. It does allow you to compare floats and integers:
1.0 == 1 #=> true
but comparing strings and integers doesn't work that way:
'1' == 1 #=> false
You have to convert it yourself:
'1'.to_i == 1 #=> true
Since your input is supposed to be an integer, we can convert it right away:
stress = gets.to_i
When entering 100enter, gets will return "100\n" and to_i will interpret that as an integer, returning 100 (to_i ignores the trailing newline, so we don't need chomp).
stress is now an integer, so you can compare it to other integers:
if stress == 100
puts "Go to server room"
elsif stress > 50
puts "Take a break"
else
puts "Continue working"
end
I've changed === to ==. For comparisons you almost always want the latter. === has special meaning in Ruby. Just avoid it for now.
The above code would already work, but you wanted a method. You could call your method stress, i.e. use the same name for the variable and the method. But it makes your code much harder to understand. So let's choose a different method name, e.g. message. While we're at it, let's also remove the repeated puts calls, so it just returns the message without printing it:
def message(stress)
if stress == 100
"Go to server room"
elsif stress > 50
"Take a break"
else
"Continue working"
end
end
You can call the above method via:
input = gets.to_i
puts message(input) # <- 'puts' is now here
I've deliberately chosen a different variable name for the input to distinguish it from the method argument.

You have a few problems here. For one, as someone already mentioned, you are not passing an argument to your method. For another, the gets method returns a String, not an integer, so if you pass the result in you are comparing a String to an integer.
Rework your method like this:
def stress(level_string)
level = level_string.to_i
if level == 100
"Go to server room"
elsif stress > 50
"Take a break"
else
"Continue working"
end
end
Now you can try:
>> level_string = gets.chomp!
>> 100
#> "100"
>> stress(level_string)
#> "Go to server room"

In your statement
p stress
you output the value of the variable named stress, not the value returned by the method stress.
You can have a method and a variable of the same name, but then you have to invoke the method using parenthesis:
p stress()
In your particular case, you would then run however into the next problem: Inside your method stress, you are in a nested scope, which means that the outside variable stress is not visible. Hence, in the statement
if stress === 100
you refer to the method stress, i.e. you have a recursion.
There are several possible solutions:
Pass the variable by parameter
Change the name of the variable
Change the name of the method
As a rule of thumb, I recommend to
never give a variable and a method the same name, and
do not access variables from outer scope inside a method
unless you have very good reasons to do otherwise.

Your variable and your method definition have the same name. Try changing:
stress = gets.chomp!
to
var = gets.chomp!
Then set up your method to accept the variable:
def stress(s)
if s.to_i == 100
p "Go to server room"
elsif s.to_i > 50
puts "Take a break"
else
p "Continue working"
end
end
p stress(var)
You can also do it close to what you were doing, just use a variable other than the name of your method:
var = gets.chomp!
def stress
if self.to_i == 100
p "Go to server room"
elsif self.to_i > 50
puts "Take a break"
else
p "Continue working"
end
end
p var.stress

Related

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'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 to run a simple Ruby script

I would like to make a program that checks to see if the number you enter is an even number. Sort of like making a leap year program but for any number divisible by 2.
Something along the lines of:
num = gets.chomp
while num != 0
if (num%2) == 0
puts 'yess'
else
puts 'nooo'
end
end
I knows there's something easy that I need to change for it to run.
(btw I just started learning Ruby yesterday!)
There are two problems here.
First being something that others have put, you need to make sure you turn the input into an integer using ".to_i" on your num variable.
Secondly, this code puts you into an infinite loop since you are using a "while" loop.
Since the number is only input once, you get stuck in the "while" loop forever no matter what the input is. Basically, "num" never stops being not 0.
You'd be better off using an if..else statement. Something like:
num = gets.chomp.to_i
if num != 0
if (num%2) == 0
puts 'yess'
else
puts 'nooo'
end
else
puts "that's 0, dude"
end
Integers have two methods for this. They are even? and odd?.
You can use this in your if statement as so:
if num.even?
puts 'yess'
else
puts 'nooo'
end
However, an easier way to write this is with ternary expressions:
puts num.even? ? "yes" : "no"
However, make sure num is an Integer. Anything coming from gets will be a String. So, you should be doing num = gets.chomp.to_i. Anything that is not a number, like "h", will return 0.
"5".to_i #=> 5
"h".to_i #=> 0

Converting string into class [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
What I do need:
I pass a string that has to set an unmutable object inside an array, but I do not know hot how to make the transition from the string that the user inputs to the object name I need.
What I am intending to do:
I am working on a conversational adventure. The key point is to have a function that creates a command prompt so the user can interact with the game. Whenever the user says "go to somewhere", there is another function called "goto" that compares whether the input is included in the exits of the place where the player is; if so, the attribute "place" for the player takes a new place.
What I did:
I made a command prompt that actually works*
loop do
print "\n >>> "
input = gets.chomp
sentence = input.split
case
when sentence[0] == "inspect"
$action.inspect(sentence[1])
when sentence[0] == "go" && sentence[1] == "to"
$action.goto(sentence[2])
when sentence[0] == "quit"
break
else
puts "\nNo le entiendo Senor..."
end
And I initialized the objects as I need them (the third attribute goes for the exits):
room = Place.new("room", "Room", [newroom], "This is a blank room. You can _inspect_ the -clock- or go to [newroom].", ["this this"])
newroom = Place.new("newroom", "New Room", [room], "This is another blank room. You can _inspect_ the -clock-", ["this this"])
Then I made a method inside the action controller that has to compare and set the places properly. (Beware: monster newbie code following. Protect you eyes).
def goto(destiny) #trying to translate the commands into variable names
if (send "#{destiny}").is_in? $player.place.exits
$player.place = send "#{sentence[2]}"
puts destiny.description
else
puts "I cannot go there."
end
end
I think you want to convert a string to constant. Well it is easy. Read an example:
string = 'Hash'
const = Object.const_get(string) #=> Hash
const.new #=> {}; <- it is an empty Hash!
But be careful. If there's no such a constant you will get uninitialized constant error. In this case your adventures will stop.
I hope I understood your question and you will understand my answer.
How to change string to object, there are few options:
Bad(eval family):
eval("name_of_your_variable = #{21+21}")
eval("puts name_of_your_variable") #42
You can see that eval can make everything. So use with caution.
However, as pointed by #user2422869 you need(be in) scope - place where your variables are saved. So above code won't run everywhere
Everytime you run following method you create another scope
def meth1
puts "defined: #{(defined? local_a) ? 'yes' : 'no'}!"
eval 'local_a = 42'
local_a += 100
eval 'puts local_a'
end
meth1
and here is output:
defined: no!
142
If you want to grab local_a from one of scopes of meth1 you need binding.
def meth2
var_a = 222
binding
end
bin = meth2
bin.eval 'var_a'
#output:
#222
About binding you can read in doc. As for scopes, I don't have good site.
Better:
hash_variable = Hash.new # or just {}
hash[your_string_goes_here] = "some value #{42}"
puts hash[your_string_goes_here]
I don't know if good or bad:
As for this: send "#{destiny}". I assume that your destiny doesn't exist, so you can use method_missing:
def method_missing arg, *args, &block
#do some with "destiny"; save into variable/hash, check if "destiny" is in right place etc.
# return something
end

simple if else statement unit conversion cm and in

For some reason this code always multiplies b (2.54) by unit_number, regardless of whether I put enter "cm" or "in":
puts "Welcome to my unit conversion calculator. This calculator can convert
between Centimeters and Inches. If you could like to convert Centimeters to Inches, write: in. If you would like to convert Inches to centimeters, write: cm."
unit = gets.to_s
puts " how many of your unit would you like to convert"
unit_number = gets.to_f
a = 0.39370079
b = 2.54
if unit == 'in'
puts "your conversion is: #{ (unit_number * a)}"
else unit == 'cm'
puts "your conversion is: #{ (unit_number * b)}"
end
gets will capture the stdin input including the trailing new line (enter), so unit won't be 'cm' and instead will be "cm\n". You will need to remove the trailing "\n" using chomp:
unit = gets.chomp
You don't need to call to_s to the return value of gets because it is already a String, and the else statement should be elsif unit == 'cm' or else if unit == 'cm'.
Your conditional invokes an else statement, when what you actually need is an elsif statement:
elsif unit == 'cm'
Basically, what you currently have is a situation where the first of your conditions (i.e., if unit == 'in') is not being met (see Pablo B's answer for reasons why). However, the second condition is met because it's an else statement. Since the first condition is always false, the else condition will always be true.
If, instead, you invoke an elsif statement, the conditional will first check whether unit == 'in'; if it's not, then it checks whether unit == 'cm'. If neither are true, then the condition will return nil.
I know exactly how do you feel. Debugging is a pain. #akhanubis already pointed you towards #chomp method needed to process your gets return value. While I assume that you wrote your code for didactic purposes, if you ever need to convert physical units for real, just install sy gem. After gem install sy, you can:
require 'sy/imperial'
7.5.inch.in :cm
#=> 19.05
42.cm.in :inch
#=> 16.5354

Resources