Different results when using regular variable assignment and mass variable assignment - ruby

Translated a function that uses mass variable assignment to a function that uses regular variable assignment
# mass assignment
def fibo_finder n
a, b = 0, 1
n.times { a, b = b, a + b }
a
end
# regular assignment
def fibo_finder2 n
a = 0
b = 1
n.times do
a = b
b = a + b
end
return a
end
puts "Must be: 3. fibo_finder: #{fibo_finder(4)} fibo_finder2: #{fibo_finder2(4)}"
puts "Must be: 13. fibo_finder: #{fibo_finder(7)} fibo_finder2: #{fibo_finder2(7)}"
puts "Must be: 55. fibo_finder: #{fibo_finder(10)} fibo_finder2: #{fibo_finder2(10)}"
Running the code results to:
Must be: 3. fibo_finder: 3 fibo_finder2: 8
Must be: 13. fibo_finder: 13 fibo_finder2: 64
Must be: 55. fibo_finder: 55 fibo_finder2: 512
At first glance, the two functions look equivalent aside from how the variables were assigned. But the results show otherwise.
Is Ruby doing something special when variables are assigned using mass assignment?
I'm using repl to run the code

Yes.
In your mass assignment example:
a, b = b, a + b
the assignments are carried out in parallel - both right-hand expressions are evaluated and then the two results are assigned to the left hand side.
In your second example:
a = b
b = a + b
a is set to b and then b is set to a + b (or, b + b because a is now equal to b)

The two assignments are not equivalent.
In the parallel assignment:
a, b = b, a + b
The right hand side are evaluated, collected an an array, and then the left hand side gets the value from this array. It's equivalent to:
arr = [b, a + b]
a, b = arr
Note that in b gets the value of a + b with the original value of a.

Suppose:
a = 1
b = 2
Then with parallel assignment,
a,b = b,a #=> [2, 1]
a #=> 2
b #=> 1
By contrast:
a = 1
b = 2
a = b #=> 2
b = a #=> 2
a #=> 2
b #=> 2
Parallel assignment is often used for swapping values of variables, to avoid the need to create a temporary variable. If it worked as in my second example you couldn't use it for that.

Related

Why is this method not returning nil when a and b are equal?

This method should return the sum of all numbers between a and b (not ordered) and should return a or b if they are equal.
def get_sum(a,b)
a < b ? (a..b).sum : (b..a).sum
end
I'm curious as to why this method doesn't return nil when a and b are equal. The ternary operator takes care of the condition where a is less than or greater than b so I'm tempted to add return a if a==b before the ternary to test if they are equal but it appears it's not necessary.
For example, if a=5 and b=5 then the value returned is 5. Could someone explain why this is please?
This is just how the < operator (or rather method) works.
a < b
It returns true if a is less than b, otherwise it returns false.
Therefore 5 < 5 returns false.
By writing a..b you create a Range object Range.new(a, b), which starts at a and goes up to and including b. Afterwards you call the sum method on this object.
Since the range 5..5 goes from 5 to and including 5, when you convert it to an array you get:
(5..5).to_a #=> [5]
And the sum method adds all numbers which are within this range. And since this range includes a single number 5, the result of this operation is 5.
(5..5).sum #=> 5
Your method could written more concisely
def get_sum(a, b)
(a < b ? a..b : b..a).sum
end
To make it return 0 when a and b are the same, you could use a exclusive range with three dots ....
def get_sum(a, b)
(a < b ? a...b : b...a).sum
end
get_sum(5, 5) #=> 0
get_sum(5, 6) #=> 5
get_sum(5, 7) #=> 11
In fact there is no Range#sum because it is inherited from Enumerable#sum
So (5..5).sum it is the same as (5..5).to_a.sum
(5..5).to_a # => [5]
That's the reason
To fix it you can do something like this (change else branch of your ternary operator to elsif)
def get_sum(a, b)
if a < b
(a..b).sum
elsif a > b
(b..a).sum
end
end
get_sum(1, 2) # => 3
get_sum(2, 1) # => 3
get_sum(5, 5) # => nil
But in my opinion get_sum(5, 5) == 5 is more clear than nil
the ternary operator has only two branches. if the condition is not truthy the latter is executed. there are no cases where no branches are executed (unless some error happens but that's another case).
you get nil only if you have only a branch and that's not executed. e.g.
def get_sum(a,b)
if a < b
(a..b).sum
end
end
for get_sum(5, 5) this evaluates to nil

Enumerator new and yield

At ruby docs I found this piece of code:
fib = Enumerator.new do |y|
a = b = 1
loop do
y << a
a, b = b, a + b
end
end
Thing is i cant understand what it does. In general it calculates fibonacci's numbers, but I have hard time understanding the details. What is y (Enumerator::Yielder)? The docs says nothing about it. What does << method do? (I know its yield alias). Why does infinite loop happen when y << a
is removed?Thanks!
Consider the following code:
fib = Enumerator.new do |y|
puts "Enter enumerator"
a = b = 1
loop do
puts "Inside loop"
y << a
puts "y: #{y.inspect}, a: #{a}, b: #{b}"
a, b = b, a + b
end
end
puts fib.take(5)
It prints:
# Enter enumerator
# Inside loop
# y: #<Enumerator::Yielder:0x000000059a27e8>, a: 1, b: 1
# Inside loop
# y: #<Enumerator::Yielder:0x000000059a27e8>, a: 1, b: 2
# Inside loop
# y: #<Enumerator::Yielder:0x000000059a27e8>, a: 2, b: 3
# Inside loop
# y: #<Enumerator::Yielder:0x000000059a27e8>, a: 3, b: 5
# Inside loop
# 1
# 1
# 2
# 3
# 5
Apparently, this output actually gives hints on all the question you’ve stated. Note, that we entered a yielder only once. Let’s dig into:
Why loop is infinite?
Because Fibonacci’s number sequence is infinite. This enumerator is intended to be used with Enumerable#take (see an example above.)
What is Enumerator::Yielder?
It is an abstraction. It’s method yield actually calls back the block of callee, passing a parameter as block parameters.
What does << method does?
Yields once. In other words, it calls back the caller code, passing it’s parameter to the caller’s block. In this particular example, it will call back each block, passing a from Yielder instance as block parameter (e as I named it there.)
Why does infinite loop happen when y << a is removed?
Because there are no yields happened. In my example, the callee will stop after yielding five (5 as parameter of take) times.

pythagorean triangle with any side equal to zero

So I have this spec I'm trying to solve for (I did not write the spec)...
it "returns false if any of the arguments are 0" do
# [0, 1, 1].permutation(3) returns all permutations of [0, 1, 1]
length = rand(0.01..100.0)
[0, length, length].permutation(3).all? { |(a,b,c)| valid_triangle?(a,b,c) }.should be_false
end
Here is my code
def valid_triangle?(a, b, c)
#A Pythagorean triple consists of three positive integers a, b, and c, such that a2 + b2 = c2.
if a > 0 && b > 0 && c > 0
one = a**2
two = b**2
three = c**2
if one+two=three
return "true"
else
return "false"
end
else
return "false"
end
end
How can I pass my spec? What am I missing?
The main problem is that when the test permutes the values of a, b and c, your method is not always checking that it is the square of the hypotenuse that equals the sum of the squares of the other two sides. For example, if a=3, b=4 and c=5, one of your tests will be 4*4 + 5*5 == 3*3. You need to sort a, b and c before checking the sum of squares, which is a good idea anyway, since the position of the hypotenuse among the parameters is not guaranteed. You could also simplify your code somewhat. Here's one way you could write it:
TOLERANCE = 0.01
def right_triangle?(a, b, c)
return false if a == 0 || b == 0 || c == 0
a, b, c = [a,b,c].sort
(a**2 + b**2 - c**2).abs < TOLERANCE
end
length = rand(0.01..100.0)
[0.0, length, length].permutation(3).all? { |(a,b,c)| right_triangle?(a,b,c)}
#=> false
[3,4,5].permutation(3).all? { |(a,b,c)| right_triangle?(a,b,c) }
#=> true
Since you are dealing with floats, you need to establish some level of tolerance when comparing values for equality. I've used an arbitrary fixed amount (0.01) for demonstration purposes.
You are returning "false" and "true" instead of false and true, also you check that one+two=three, when you should check one+two==three (an equals check, not an assignment)
def valid_triangle?(a, b, c)
#A Pythagorean triple consists of three positive integers a, b, and c, such that a2 + b2 = c2.
if a > 0 && b > 0 && c > 0
one = a**2
two = b**2
three = c**2
if one+two == three
return true
else
return false
end
else
return false
end
end

Remove value from Variable

This is probably a very trivial question, but..
Assuming we have a variable 'a' that has a value of 10
Then we have a variable 'b' that has a value of 5
Can I remove the value of 'b' from 'a' (leaving 5) and change the value of a each time?
In my head I think of it as:
a - b = 5
a - b = 0
a - b = -5
etc etc..
Thanks!
Consider this:
a = 10
b = 5
a = a - b
Now the value of a is 5. This is because Ruby evaluates what is on the right side of the assignment operator = first and then assigns that value to whatever is on the left side.
#Another way to put it
a = (a - b)
There is also a shortcut since this type of math is very common
a = a - b
a -= b
# These are both the same
If you want to do this a certain number of times, try a loop.
a = 10
b = 5
3.times do {a -= b}
# a is now -5
For further basic Ruby learning, I would suggest Try Ruby to get you started on concepts as I mentioned above. (Type "next" there to get started)

How to optimize the ruby code iterates several integers in a range?

New to Ruby, and trying to find a 3 digits number "abc":
for a in 0..9
for b in 0..9
for c in 0..9
if a*a+b*b+c*c == (100*a+10*b+c)/11.0
puts "#{a}#{b}#{c}"
end
end
end
end
This is too lengthy, is any way to optimize it, or write it in another "ruby" way?
Solution from: Wolfram Alpha :)
Here's another fun solution. Not really faster, just more compact and perhaps more ruby-like if that was what you were looking for:
(0..9).to_a.repeated_permutation(3).select { |a,b,c|
a*a+b*b+c*c == (100*a+10*b+c)/11.0
}
=> [[0, 0, 0], [5, 5, 0], [8, 0, 3]]
This is equivalent to finding a,b,c such that
100*a + 10*b + c = 11 * (a*a + b*b +c*c)
i.e. 100*a + 10*b + c must be divisible by 11. Simple number theory tells you that when a,b,c are digits, this means that
`a + c - b`
must be a multiple of 11 so
`a + c = b or a + c = 11 +b`
So for a given values of a and b you only need to check two values of c : b -a and 11 +b -a rather than 10. You can cut the search space in two again: if a > b you only need to check the latter of those two values and if a <= b you need only check the former.
Thus instead of checking 1000 triplets of numbers you should only need to check 100, which should be 10 times faster.
for a in 0..9
for b in 0..9
if a > b
c = 11 +b -a
else
c = b - a
end
if a*a+b*b+c*c == (100*a+10*b+c)/11.0
puts "#{a}#{b}#{c}"
end
end
end

Resources