Error while making calculator on Ruby? - ruby

While trying to make a calculator in Ruby, using TextWrangler's #! function (Compiled in Terminal) I encountered an error. When I add 2+2 the answer it returns is 2.0. I tried other functions, for example calculating a specific percentage of a certain number, no matter what I tried doing the answer was always 0.0. I checked the syntax, using the #! function, and there were no errors. I know I made it unnecessarily wrong, but it is easier to read it this way for me.
loop do
print
equation = gets.chomp
if equation.include?"^"
exponent_e = equation.split("^")
result_e = equation[0].to_f ** equation[1].to_f
puts "#{equation} = #{result_e}"
elsif equation.include?"%"
percent_e = equation.split("%")
number = equation[0].to_f / 100
result_p = number * equation[1].to_f
puts "#{equation} = #{result_p}"
elsif equation.include?"/"
equation.split("/")
result_d = equation[0].to_f / equation[1].to_f
puts "#{equation} = #{result_d}"
elsif equation.include?"*"
equation.split("*")
result_m = equation[0].to_f * equation[1].to_f
puts "#{equation} = #{result_m}"
elsif equation.include?"+"
equation.split("+")
result_a = equation[0].to_f + equation[1].to_f
puts "#{equation} = #{result_a}"
elsif equation.include?"-"
equation.split("-")
result_s = eqaution[0].to_f - equation[1].to_f
puts "#{equation} = #{result_s}"
end
end

You're not storing the split in a variable. You need to do something like this:
elsif equation.include?"+"
res = equation.split("+")
result_a = res[0].to_f + res[1].to_f

Related

assigning a method result to a variable in ruby

I'm sure it would be hard to find an easier question, but I'm a complete newbie. I have searched extensively and for some reason can't find the answer to this. Here's my code:
puts "Enter F for Fahrenheit and C for Celsius."
x = gets.chomp.downcase
def ftoc(fahrenheit)
(fahrenheit.to_f - 32.0) * (5.0 / 9.0)
end
if x == "f"
puts "Enter your temp:"
temp = gets.chomp.to_i
ftoc temp
elsif x == "c"
puts "Enter your temp:"
temp = gets.chomp.to_i
ctof temp
else
puts "That does not compute."
end
I'm just trying to get the returned result of the method into a variable so I can use it elsewhere....
Remember that calls like ctof temp just initiate a method and then, as you're not putting the result anywhere, discard it immediately.
To clean up this code let's organize it better:
# Temperature conversion method
def ftoc(fahrenheit)
(fahrenheit.to_f - 32.0) * (5.0 / 9.0)
end
# User input method
def temperature_prompt!
puts "Enter F for Fahrenheit and C for Celsius."
x = gets.chomp.downcase
case (x)
when "f"
puts "Enter your temp:"
temp = gets.chomp.to_i
ftoc temp
when "c"
puts "Enter your temp:"
temp = gets.chomp.to_i
ctof temp
else
puts "That does not compute."
end
end
Now you can make use of the fact that in Ruby things like if and case actually return values. In this case it's the value of the last thing to execute in each block, so that result isn't discarded, it's just passed along:
temp = temperature_prompt!
If you enter an invalid value you get the result of puts which is conveniently nil.
Here's something to consider: Ruby is very good at parsing arbitrary text if you can describe the patterns. Here's a simple input routine:
def temperature_prompt!
puts "Enter degrees (e.g. 8F, 2C)"
case (input = gets.chomp.downcase)
when /(\d+)f/
ftoc $1
when /(\d+)c/
ctof $1
else
puts "That does not compute."
end
end
You could add to those patterns to allow things like -2C and 3.4°F if you wanted.

call a method from another file in ruby

Hello I'm new to programming and I started with ruby. I'm trying to do my first program. I found online this code that generate a dice roll
class Die
def initialize(sides)
#sides = sides
end
def generate_die_roll
rand(#sides) + 1
end
def roll(number=1)
roll_array = []
number.times do
roll_array << generate_die_roll
end
total = 0
roll_array.each do |roll|
new_total = total + roll
total = new_total
end
total
end
end
and I would like to use in they way that if the number generated is inferior o equal to another print something otherwise something else.
It's probably very easy but i'm trying in every way and now I will need some help please.
that's my code:
require "./newdado.rb"
energia_vitale = 30
puts "Lancia un dado scrivendo (D) da sommare alla tua Energia Vitale iniziale di #{energia_vitale} punti"
scelta = gets.chomp
case scelta
when "D"
SIX_SIDED_DIE = "#{Die.new(6)}"
values = Array[]
values.push(SIX_SIDED_DIE.roll)
puts values
if values < 2
puts "c"
else puts "b"
end
end
when I run it i receive this error
C:/Users/fix/workspace/D&D Ruby/energia vitale.rb:11:in <main>': undefined methodroll' for "#":String (NoMethodError)
Sorry to bother the community with this beginner problem
Why as string?
this line
SIX_SIDED_DIE = "#{Die.new(6)}"`
should be something like
die = Die.new(6)
then you can do die.roll

Can this be optimized without using a global variable?

I've recently begun learning ruby and I'm trying to avoid using global variables where possible. I wrote the below program which accepts user input and outputs math tables of the users choice (currently just +, * but to be expanded upon). I'm following suggestions from https://adriann.github.io/programming_problems.html to get me learning.
class User_input
.
# multiply
def User_input.mult1_to_12
by = (0..12).each do | range |
result = $choice_int * range
puts "#{$choice_int} x #{range} = #{result}"
end
end
# add
def User_input.add1_to_12
add = (0..12).each do | range |
result = $choice_int + range
puts "#{$choice_int} + #{range} = #{result}"
end
end
# accepts user input
puts "Please enter the tables you require (1-12): "
$choice_int = gets.to_i
puts "You have selected #{$choice_int}"
puts "Which tables do you require (+ - * /): "
choice_method = gets.chomp
puts "the method you have chosen is #{choice_method}"
if choice_method == "*"
User_input.mult1_to_12
elsif
choice_method == "+"
add1_to_12
end
end
You will note that I am currently using a global variable for $choice. Can someone with more experience suggest a more optimal solution. Please feel free to tear my code apart : ) Thanks.
Methods can accept parameters, for example:
# add numbers
def add(a,b)
a+b
end
puts add(1,2)
# will output 3
Here's a simple modification to your code using parameters:
class UserInput
# multiply
def self.mult1_to_12(choice_int)
(0..12).each do | range |
result = choice_int * range
puts "#{choice_int} x #{range} = #{result}"
end
end
# add
def self.add1_to_12(choice_int)
(0..12).each do | range |
result = choice_int + range
puts "#{choice_int} + #{range} = #{result}"
end
end
end
# accepts user input
puts "Please enter the tables you require (1-12): "
choice_int = gets.to_i
puts "You have selected #{choice_int}"
puts "Which tables do you require (+ - * /): "
choice_method = gets.chomp
puts "the method you have chosen is #{choice_method}"
if choice_method == "*"
UserInput.mult1_to_12(choice_int)
elsif choice_method == "+"
UserInput.add1_to_12(choice_int)
end
And here's a bit prettier solution that can also handle - and / (and a bunch of other operations provided by Ruby's Fixnum):
class UserInputProcessor
# define accessors to instance variables
attr_accessor :choice_int, :choice_method
def process
(0..12).map do |range|
if choice_method.eql?('/')
next if range.eql?(0) # need to skip X/0 to avoid division by zero
range = range.to_f # need to convert range to float to get float results
end
"#{choice_int} #{choice_method} #{range.to_i} = #{choice_int.send(choice_method, range)}"
end
end
end
handler = UserInputProcessor.new
print "Please enter the tables you require (1-12): "
handler.choice_int = gets.chomp.to_i
puts "You have selected #{handler.choice_int}"
print "Which tables do you require (+ - * /): "
handler.choice_method = gets.chomp
puts "the method you have chosen is #{handler.choice_method}"
puts "And here are the results:"
puts handler.process.join("\n")

Replace string with ruby code

I worte an simple programm to replace text within (( )) with the user input:
If i have for example this this text:
i hab an terrible ((userinput1)) last ((userinput2)) in a horrible ((userinput3))
I tried first to replace the (( with #{ and the )) with }
str1 = gets.chomp
str2 = str1.clone
a = 0
begin
s = str2.index('((', a)
str2[s..s+1] = '#{'
a = a + s + 1
end until str2.length < a
b = 0
begin
s = str2.index('))', b)
str2[s..s+1] = '}'
b = b + s + 1
end until str2.length < b
userinput1 = gets.chomp
userinput2 = gets.chomp
userinput3 = gets.chomp
puts str2
But somehow ruby dont validates the userinputs, instead i get:
i hab an terrible #{userinput1} last #{userinput2} in a horrible #{userinput}
I think the problem is that in my code i wrote:
str2[s..s+1] = '#{'
instead of
str2[s..s+1] = "#{"
because so all my remaining code is an object until it is closed with }. SO what can i do ? I hope you understood my issue?
str1 = gets.chomp
word = str1.scan(/\(\(\w+\)\)/)
word.each do |word|
str1.gsub(word, "what the fuck")
end
puts str1
Nice try. But there's a simpler way. Much simpler :) Basically, you just have to collect user inputs. There are methods for replacing string parts already.
userinput1 = gets.chomp
userinput2 = gets.chomp
userinput3 = gets.chomp
template = "i had a terrible ((userinput1)) last ((userinput2)) in a horrible ((userinput3)"
result = template.gsub('((userinput1))', userinput1).
gsub('((userinput2))', userinput2).
gsub('((userinput3))', userinput3)
# ^^ replacing happens here
puts result

Trouble with combined logic in ruby

writing a simple monte carlo simulation of a neutron beam. Having trouble with the geometry logic (whether something is in one environment or another). My issue is that Ruby seems to be processing the conditions sequentially and keeping the first value it comes to.
The code below illustrates this quite nicely:
def checkPosition(*args)
polyCylRad = 2.5
polyCylFr = 15
polyCylB = -2.0
borPolyBoxL = 9.0 / 2
pbCylRad = 3.0
pbBoxL = 10.0 / 2
cdBoxL = 9.5 / 2
position = Array.new
material = String.new
args.each do |item|
position << item.inspect.to_f
end
xSquared = position.at(0) ** 2
ySquared = position.at(1) ** 2
zSquared = position.at(2) ** 2
modX = Math.sqrt(xSquared)
modY = Math.sqrt(ySquared)
modZ = Math.sqrt(zSquared)
puts xSquared
puts Math.sqrt(ySquared + zSquared) <= polyCylRad
puts (position.at(0) >= polyCylB)
puts (position.at(0) <= polyCylFr)
puts (position.at(0) >= polyCylB)and(position.at(0) <= polyCylFr)
puts (position.at(0) <= polyCylFr)and(position.at(0) >= polyCylB)
puts zSquared
polyCylinder = (Math.sqrt(ySquared + zSquared) <= polyCylRad)and((position.at(0) >= polyCylB)and(position.at(0) <= polyCylFr) )
puts polyCylinder
borPolyBox = ((modX <= borPolyBoxL)or(modY < borPolyBoxL)or(modZ <= borPolyBoxL)) and not((modX >= cdBoxL)or(modY >= cdBoxL)or(modZ >= cdBoxL)) and not(Math.sqrt(ySquared + zSquared) <= polyCylRad)
puts borPolyBox
cadmiumShield = ((modX <= cdBoxL)or(modY < cdBoxL)or(modZ <= cdBoxL)) and not((modX >= pbBoxL)or(modY >= pbBoxL)or(modZ >= pbBoxL)) and not(Math.sqrt(ySquared + zSquared) <= polyCylRad)
puts cadmiumShield
leadShield = ( ((modX <= pbBoxL)or(modY <= pbBoxL)or(modZ <= pbBoxL)) or ((position.at(0) <= ployCylFr)and(Math.sqrt(ySquared + zSquared) <= pbCylRad)) ) and not(Math.sqrt(ySquared + zSquared) <= polyCylRad)
puts leadShield
if (polyCylinder) : material = "poly"
elsif(borPolyBox) : material = "borPoly"
elsif(cadmiumSheild) : material = "cd"
elsif(leadSheild) : material = "pb"
elsif(material == nil) : position = Array.new
end
thisEnvironment = Array.new
thisEnvironment << position << material
puts thisEnvironment.at(0)
puts thisEnvironment.at(1)
end
checkPosition(40, 0, 0)
call the code whatever you want, but give it *args as an argument (I am lazy and may want to add more args in the future) then call it with 3 floats, wrt the geometry set up in the logic and you'll see what I mean.
My question is: how do I get it to work like it should (ie evaluating the logic correctly) without a whole bunch of nested if's? (which is what I am about to remake, however it is a nightmare to read and memory is cheap.)
You've typed "Sheild" a few times where you probably meant "Shield"
In the context you're using them, you should be using && instead of and, || instead of or, and ! instead of not. The reason is that or and and have such a low precedence that they will cause your assignment operators to not work the way you want. For example,
a = b and c
evaluates as
(a = b) and c
Such that a is always assigned the value b, and then in the result is truthy, c is evaluated (and discarded). On the other hand,
a = b && c
evaluates as
a = (b && c)
Which is what you want in this code.
Beyond that, I would move all of this code into a class, so that I can create lots of little methods for things:
class PositionChecker
def initialize(*args)
#x, #y, #z = *args
end
def checkPosition
...
end
end
Look for opportunities to replace local variables in checkPosition with method calls. For example, you could move borPolyBox into its own method (once all of the values it uses are methods of their own):
class PositionChecker
...
def borPolyBox
((modX <= borPolyBoxL)||(modY < borPolyBoxL)||(modZ <= borPolyBoxL)) && !((modX >= cdBoxL)||(modY >= cdBoxL)||(modZ >= cdBoxL)) && !(Math.sqrt(ySquared + zSquared) <= polyCylRad)
end
...
end
Once you've got all of these predicates as their own method, you can create a method to determine the material, like so:
def material
[
[:polyCylinder, 'poly'],
[:borPolyBox, 'borPoly'],
[:cadmiumShield, 'cd'],
[:leadShield, 'pb'],
].each do |method, name|
return name if send(method)
end
nil
end
And one for the position:
def position
[#x, #y, #z] if material
end
Continue along this line until nothing is left but a bag of teeny, focused methods.
Change all and and or to && and ||.
Never seen anyone actually use array.at(index) instead of array[index] before.
I also recommend against *args in favor of a Hash parameter as a kind of named parameters
def test(params)
x = params[:x] || raise("You have to provide x!")
y = params[:y] || raise("You have to provide y!")
z = params[:z] || raise("You have to provide z!")
puts x, y, z
end
and call it with (Ruby 1.9+ syntax)
test({x: 42, y: 4711, z: 93})
42
4711
93
Try using && instead of and, || instead of or and ! instead of not.
Your issue is probably a precedence one - read this article for more information.
Looking at your code, the way you are setting this up is to have the four "material" variables as booleans. Then you feed those bools into an if-elsif-else block.
The problem with this is that the very first if that returns true is going to exit the if-elsif-else block. If that's what you mean by keeping the first value it comes to, than that's an extremely predictable outcome.

Resources