Ruby interpreter ignors conditions of elsif statements - ruby

I wroe this method in a class :
def OpCodes
if #asm[0] == "ADD"
opcode = "111"
elsif #asm[0] == "CMP"
opcode = "110"
elsif #asm[0] == "SHL"
opcode = "101"
elsif #asm[1] == "SHR"
opcode = "100"
elsif #asm[1] == "NOT"
opcode = "011"
elsif #asm[1] == "XOR"
opcode = "010"
elsif #asm[1] == "OR"
opcode = "001"
elsif #asm[1] == "AND"
opcode = "000"
else
opcode = "ERROR"
end
#opcode = opcode
return #opcode
end
And when I run the code, and for example I give it "ADD 1, 3" string, it works and returns "111". But, when I use other commands, it returns "ERROR"

You could rewrite this to use a simple lookup table:
OPCODES = {
ADD: 0b111,
CMP: 0b110,
SHL: 0b101,
SHR: 0b100,
NOT: 0b011,
XOR: 0b010,
OR: 0b001,
AND: 0b000
}.freeze
OPCODE_ERROR = "ERROR".freeze
def opcode(instruction)
OPCODES[instruction.to_sym] or OPCODE_ERROR
end
Note it's much better to use native binary numbers than string representations of same, so I'm using the 0bNNN notation here. 0b001 is 1, 0b010 is 2. You can convert back to a string if you want using n.to_s(2) for base-2.
Additionally you should never name methods CamelCase style, those are reserved for class and module names. Instead use underscore_style.
It looks like you're not parsing your input properly. Try with a regular expression:
instruction, a, b = input.scan(/\A(\S+)\s+(\S+),\s*(\S+)/)[0]
For something like "ADD 1,2" you will get those values broken out into three variables you can use.

Related

What does this ruby code do?

I have code here that verifies the number input for zero and floats:
def integer?(input)
input.to_i.to_s == input
end
def float?(input)
input.to_f.to_s == input
end
def valid_number?(input)
integer?(input) || float?(input)
end
loop do # main loop
number1 = ''
loop do
prompt(messages('first_number', LANGUAGE))
number1 = Kernel.gets().chomp()
if valid_number?(number1)
break
else
prompt(messages('not_a_valid_number', LANGUAGE))
end
end
number2 = ''
loop do
prompt(messages('second_number', LANGUAGE))
number2 = Kernel.gets().chomp()
if valid_number?(number2)
break
else
prompt(messages('not_a_valid_number', LANGUAGE))
end
end
end
result = case operator
when '1'
number1.to_i() + number2.to_i()
when '2'
number1.to_i() - number2.to_i()
when '3'
number1.to_i() * number2.to_i()
else
number1.to_f() / number2.to_f()
end
prompt("The answer is: #{result}")
What does this code do in layman's term or in an explanation that a dummy can understand?
def integer?(input)
input.to_i.to_s == input
end
def float?(input)
input.to_f.to_s == input
end
def valid_number?(input)
integer?(input) || float?(input)
end
Any help here? I would appreciate if you could explain it line by line thanks!
Sorry newbie here!
In those functions input is transformed to a number (integer using to_i or float using (to_f)) and then back to string (with to_s). Then the result of those transformations is compared with the input.
It verifies if an input is a number, because if it is not, transformed string will be not equal to the original one.
For instance:
$ "a".to_i.to_s
=> "0"
because to_i returns 0 if string cannot be parsed to an integer (http://ruby-doc.org/core-2.0.0/String.html).
All it's doing is converting a string to an integer / float, converting it back to a string and comparing it to the input string. If the input was a valid integer / float, its converted value will be identical to the input. If, however the input was not a valid number, casting it to an integer or float will turn it into a zero, which when compared to the original input will be different. Here's an example:
irb(main):012:0> "abc".to_i.to_s
=> "0"
irb(main):013:0> "123".to_i.to_s
=> "123"
So as you can see the non-numeric input would fail the check.

Why doesn't `Range#cover?` raise an exception when comparison fails?

Given that Time objects cannot be compared with Fixnum without explicit casting:
0 <= Time.now # => ArgumentError: comparison of Fixnum with Time failed
Time.now <= 10000000000 # => ArgumentError: comparison of Time with 10000000000 failed
and what the documentation for Range#cover? says,
cover?(obj) → true or false
Returns true if obj is between the begin and end of the range.
This tests begin <= obj <= end when exclude_end? is false and begin <= obj < end when exclude_end? is true.
I expect:
(0...10000000000).cover?(Time.now) # => false
to raise an exception rather than silently return false. Why doesn't it raise an exception?
It is understandable that, with explicit casting, the comparison works:
(0...10000000000).cover?(Time.now.to_i) # => true
The doc doesn't mention an implementation detail. range_cover is implemented in terms of r_less (via r_cover_p). And r_less comment says:
/* compares _a_ and _b_ and returns:
* < 0: a < b
* = 0: a = b
* > 0: a > b or non-comparable
*/
Here is the source of r_cover_p:
static VALUE
r_cover_p(VALUE range, VALUE beg, VALUE end, VALUE val)
{
if (r_less(beg, val) <= 0) {
int excl = EXCL(range);
if (r_less(val, end) <= -excl)
return Qtrue;
}
return Qfalse;
}
As we can see, a positive number returned from either of r_less invocations will result in a Qfalse.
Now, the reason why the doc doesn't mention it, I think, is to keep it light. Normally (99.9999% of cases), you're supposed to compare comparable things, right? And in the odd case you don't, you still get a correct answer ("this Time does not belong to this range of integers").
I am pretty sure both .include? and .cover? uses the case quality operator (===) so the value you get is the same as:
p Time.now === 1000 #=> false

How can I avoid error when setting elsif range condition?

def Summer
#summer = true
puts "Your fruit are ripe for the picking."
if #tree_age == 1..5 && #tree_age > 0
#oranges = 5
elsif #tree_age == 6..15
#oranges = 20
else
#oranges = 50
end
end
I'm trying to ensure a tree between a certain age range gives x oranges, however I'm stuck with the following error referring to my elsif statement:
Orange_tree.rb:14: warning: integer literal in conditional range
I have also tried using an if greater than && less than conditional statement, can somebody please explain what this error means, and how to reach my solution.
You have a few problems:
You'll want to put your ranges in parenthesis when other operators or methods are nearby. Your current error comes from Ruby parsing elsif #tree_age == 6..15 differently than you expect - it's treating it as (1 == 6)..15, and false..15 obviously doesn't make any sense.
To test a number is within a range, use (1..5) === num, not num == (1..5). Range#=== is defined to test that the Range includes the right hand side, while Fixnum#== and Fixnum#=== both just test that the right hand side is numerically equivalent.
You don't need to test #tree_age > 0. You're already testing that it's in 1..5.
You could also consider a case statement for this, which can be a bit easier to read. case does its comparisons using ===.
#oranges = case #tree_age
when 1..5 then 5
when 6..15 then 20
else 50
end
You should use include? instead of == to determine if the given number is within the range:
def Summer
#summer = true
puts "Your fruit are ripe for the picking."
if (1..5).include?(#tree_age) && #tree_age > 0
#oranges = 5
elsif (6..15).include? #tree_age
#oranges = 20
else
#oranges = 50
end
end
==:
Returns true only if obj is a Range, has equivalent begin and end
items (by comparing them with ==), and has the same exclude_end?
setting as the range.
Which is obviously not the case.
The problem is with the lines that say == with a range.
if ( 10 == 1..11) # throws integer literal in conditional range warning
puts "true"
end
If you did this instead
if ( 10.between?(1, 11))
puts "true"
end

Why does changing the order of conditionals break my code?

I have created a ping-pong test in Ruby. It monkey patches the Fixnum class with a new method, ping_pong, that loops over a range (0..self), checks some conditions on each element, and constructs an array of the results.
The resulting array will have Ping for numbers in the range divisible by 3, Pong for numbers divisible by 5, and Ping-Pong for numbers divisible by both.
My question now is, why does the code only work if the part:
elsif (num.%(3) == 0) && (num.%(5) == 0) array.push("Ping-Pong")
is ahead of the other elsif statements? I tried putting it after the other elsifs but it didn't work.
Here's my code:
class Fixnum
define_method(:ping_pong) do
array = [0]
total = (0..self)
total = total.to_a
total.each() do |num|
if (num == 0)
array.push(num)
elsif (num.%(3) == 0) && (num.%(5) == 0)
array.push("Ping-Pong")
elsif (num.%(3) == 0)
array.push("Ping")
elsif (num.%(5) == 0)
array.push("Pong")
else
array.push(num)
end
end
array
end
end
When you have multiple if/elsif blocks chained together, only one of them will run, and the first block to have a true condition will be the one to be run. So, the order of the blocks matters. For example:
if true
puts 'this code will run'
elsif true
puts 'this code will not run'
end
Even though the conditions for those blocks are both true, only the first one is run. If you want to have both run, use two separate if blocks, like this:
if true
puts 'this code will run'
end
if true
puts 'this code will also run'
end

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