What does RubyMonk's calculate method do? - ruby

I'm learning Ruby on RubyMonk right now and am struggling with the code below: the calculate method -> def calculate(*arguments). Is it possible that you can explain to me each line of code? I think I know what each code line's syntax, but the logic doesn't make any sense to me?
def add(*numbers)
numbers.inject(0) { |sum, number| sum + number }
end
def subtract(*numbers)
sum = numbers.shift
numbers.inject(sum) { |sum, number| sum - number }
end
def calculate(*arguments)
# if the last argument is a Hash, extract it
# otherwise create an empty Hash
options = arguments[-1].is_a?(Hash) ? arguments.pop : {}
options[:add] = true if options.empty?
return add(*arguments) if options[:add]
return subtract(*arguments) if options[:subtract]
end
for example, the options is killing me...I don't know what result returns from the two lines...
Thank you so much!

options = arguments[-1].is_a?(Hash) ? arguments.pop : {}
arguments is an array. A negative array index starts counting from the end of the array, with -1 being the last element. So arguments[-1] is the last argument passed to the method. is_a?(Hash) tests if the argument is a hash. '?' and ':' make up the ternary operator. If the last argument is a hash, pop it from the array. If it is not, then just give us an empty hash.
options[:add] = true if options.empty?
This line is equivalent to:
if options.empty?
options[:add] = true
end
What it's saying here is that if the caller didn't specify what operation they wanted, then assume they wanted to perform addition.
return add(*arguments) if options[:add]
Call the add method and return the result if options[:add]. If the caller specified addition, or if the caller did not specify any operation, then this line will be executed and the method will return here. Nothing else will be executed.
return subtract(*arguments) if options[:subtract]
Call the subtract method and return the result if options[:subtract]. If the caller specified subtraction (and did not specify addition), then this line will be executed and the method will return here. Nothing else will be executed.
Note that it's possible to reach the end of this method without triggering either of the explicit return statements, basically by passing { :operation_not_supported => true } as the last argument. In which case, you get ruby's implicit return, which would be the value of the last line executed. Which would be the one that retrieves the options hash from the argument array. Short version: specifying an unsupported operation when you call this method will have the calculate method return your options hash.

It's just an implementation of this usage:
calculate(1, 2, 3, add: true) #=> 6 (1 + 2 + 3)
calculate(1, 2, 3, subtract: true) #=> -4 (1 - 2 - 3)
calculate(1, 2, 3) #=> 6 (option defaults to {add: true})

Related

What is the difference between each and any to find a prime number in Ruby

There are two completely identical methods, only one uses each and the other any, so what is the difference between each and any?
first program code (each using):
def is_prime?(num)
Math.sqrt(num).floor.downto(2).each {|i| return false if num % i == 0}
true
end
number = gets.chomp.to_i;
puts is_prime?(number);
second program code (any using):
def is_prime?(num)
Math.sqrt(num).floor.downto(2).any? {|i| return false if num % i == 0}
true
end
number = gets.chomp.to_i;
puts is_prime?(number);
The .each method iterates through all elements of a data structure, most commonly an array or a hash, and calls the given block once for each element. If no block is given, it returns an enumerator, i.e. an instance of the Enumerator class that can then be used to "manually" iterate through the data structure.
The .any? method tests every element of a data structure against the given condition and returns true if one or more of them match or pass the test. Otherwise, it returns false. There are more details to it, so please check it from the official documentation.
General tips using Ruby
Based on this code, there are a couple of suggestions. First, you should not use return in a block as it makes the method return that value, too, and might not execute the block for more iterations.
Second, you don't need trailing semicolons at the ends of lines.
Short analysis of your code
Both your functions seem to work, at least for some numbers, but it seems that you might not exactly know why. Let's take a closer look at it.
Let's assume num = 5, for example.
First function, the block variable i gets assigned to a value of 2, meaning the block does not use the return false because the test num % i == 0 fails. Instead, the function returns true from the next line.
Second function, the block variable i also gets assigned to a value of 2, but again, as the test num % i == 0 fails the result of .any? is false, and the function returns true from the next line.
Now, let's assume num = 4.
First function, the block variable i gets again assigned to a value of 2, and since this time the test num % i == 0 passes, the return false inside the block is executed making the function return false.
Second function, the block variable i also gets again assigned to a value of 2, and since the test num % i == 0 passes here as well, the return false inside the block is executed making the function return false.
Without the return false in your second function, the function would always return true because you would not check the value returned by .any?, and the function's next line would be executed returning true.
Mechnicov offers simpler alternatives how to make your function more understandable.
each is the iterator that go through the array from beginning to end
Its main purpose is just to pass through the array (or other collection) and perform some actions that were specified in the block. For example, it can be a rendering of HTML partial or making HTTP requests or something else
There are also many other iterators that have specific tasks. These are such as map, select, any? and others
You used it wrong way. You're not using the full power of a specific iterator
But you can do it like this:
def prime?(num)
!Math.sqrt(num).floor.downto(2).any? { |i| num % i == 0 }
end
or like this
def prime?(num)
Math.sqrt(num).floor.downto(2).all? { |i| num % i != 0 }
end

Write a method returns argument to string

Coding exercise. Won't run. Please help debug my understanding :)
def my_to_s(num_one, num_three)
return num_three + num_one
end
my_to_s(1+3)
As #sebastian-palma says, you need to pass two arguments in separated by commas. So:
my_to_s(1, 3)
However, your current code simply sums the two numbers. You also need to convert the result to a string. So this would work:
def my_to_s(num_one, num_two)
result = num_one + num_two
return result.to_s
end
However, you don't need to add the return as Ruby always returns the result of the last statement in a method. Also you can combine the statement into one line. So:
def my_to_s(num_one, num_two)
(num_one + num_two).to_s
end
my_to_s(1, 3) # --> "4"

Simple way to understand returning from a block in ruby

My code is supposed to print integers in an array.
odds_n_ends = [:weezard, 42, "Trady Blix", 3, true, 19, 12.345]
ints = odds_n_ends.select { |x| if x.is_a?(Integer) then return x end }
puts ints
It gives me an error in the 2nd line - in 'block in <main>': unexpected return (LocalJumpError)
When I remove the return, the code works exactly as desired.
To find the mistake in my understanding of blocks, I read related posts post1 and post2. But, I am not able to figure out how exactly are methods and blocks being called and why my approach is incorrect.
Is there some call stack diagram explanation for this ? Any simple explanation ?
I am confused because I have only programmed in Java before.
You generally don't need to worry exactly what blocks are to use them.
In this situation, return will return from the outside scope, e.g. if these lines were in a method, then from that method. It's the same as if you put a return statement inside a loop in Java.
Additional tips:
select is used to create a copied array where only the elements satisfying the condition inside the block are selected:
only_ints = odds_n_ends.select { |x| x.is_a?(Integer) }
You're using it as a loop to "pass back" variables that are integers, in which case you'd do:
only_ints = []
odds_n_ends.each { |x| if x.is_a?(Integer) then only_ints << x end }
If you try to wrap your code in a method then it won't give you an error:
def some_method
odds_n_ends = [:weezard, 42, "Trady Blix", 3, true, 19, 12.345]
ints = odds_n_ends.select { |x| if x.is_a?(Integer) then return true end }
puts ints
end
puts some_method
This code output is true. But wait, where's puts ints??? Ruby didn't reach that. When you put return inside a Proc, then you're returning in the scope of the entire method. In your example, you didn't have any method in which you put your code, so after it encountered 'return', it didn't know where to 'jump to', where to continue to.
Array#select basically works this way: For each element of the array (represented with |x| in your code), it evaluates the block you've just put in and if the block evaluates to true, then that element will be included in the new array. Try removing 'return' from the second line and your code will work:
ints = odds_n_ends.select { |x| if x.is_a?(Integer) then true end }
However, this isn't the most Ruby-ish way, you don't have to tell Ruby to explicitly return true. Blocks (the code between the {} ) are just like methods, with the last expression being the return value of the method. So this will work just as well:
ints = odds_n_ends.select { |x| if x.is_a?(Integer) } # imagine the code between {} is
#a method, just without name like 'def is_a_integer?' with the value of the last expression
#being returned.
Btw, there's a more elegant way to solve your problem:
odds_n_ends = [:weezard, 42, "Trady Blix", 3, true, 19, 12.345]
ints = odds_n_ends.grep(Integer)
puts ints
See this link. It basically states:
Returns an array of every element in enum for which Pattern ===
element.
To understand Pattern === element, simply imagine that Pattern is a set (let's say a set of Integers). Element might or might not be an element of that set (an integer). How to find out? Use ===. If you type in Ruby:
puts Integer === 34
it will evalute to true. If you put:
puts Integer === 'hey'
it will evalute to false.
Hope this helped!
In ruby a method always returns it's last statement, so in generall you do not need to return unless you want to return prematurely.
In your case you do not need to return anything, as select will create a new array with just the elements that return true for the given block. As ruby automatically returns it's last statement using
{ |x| x.is_a?(Integer) }
would be sufficient. (Additionally you would want to return true and not x if you think about "return what select expects", but as ruby treats not nil as true it also works...)
Another thing that is important is to understand a key difference of procs (& blocks) and lambdas which is causing your problem:
Using return in a Proc will return the method the proc is used in.
Using return in a Lambdas will return it's value like a method.
Think of procs as code pieces you inject in a method and of lambdas as anonymous methods.
Good and easy to comprehend read: Understanding Ruby Blocks, Procs and Lambdas
When passing blocks to methods you should simply put the value you want to be returned as the last statement, which can also be in an if-else clause and ruby will use the last actually reached statement.

iterate array combination method with .any method

Is there anyway to iterate through different combinations of arrays?
I'm writing a program that returns true if the largest number in an array can be the sum of any of the members of the array.
This is my code: (forgive me, i learned how to program 2 weeks ago for the first time)
def ArrayAdditionI(arr)
arr=arr.sort
largest=arr.pop
n=arr.length
for i in 1..n
if arr.combination(i).to_a.any? {|array| array.inject(:+)==largest}
return true
else
return false
end
end
end
i tested it for
a = [1,2,3,4]
and it returned false even though clearly, 3+1 = 4.
When I change the above code from arr.combination(i) to arr.combination(2) its returns true. So I'm guessing it has something to do with the combination method not being able to be looped. Any suggestions?
You have return false in the wrong place. As it is, false is returned is there is no combination of one element (i.e., one element other than the one you've removed after sorting) that sums (i.e., is equal to) largest. Rather you want to return false only if no combination of any size sums to largest. This is what you need:
def max_match?(arr)
arr=arr.sort
largest=arr.pop
n=arr.length
for i in 1..n
return true if arr.combination(i).any? {|array|
array.inject(:+)==largest}
end
false
end
arr = [1,4,7,3,5,2,13]
max_match?(arr) #=> true
arr = [1,3,4,7,59]
max_match?(arr) #=> false
A few notes:
lower case letters and optional underscores are used for names of methods. You can also put a question (?) or explanation (!) mark at the end. Here a question mark seems appropriate.
to_a is not needed. Enumerable methods (e.g., any?) can have Enumerators as receivers.
sorting is expensive and unnecessary. Instead, just get the max value and remove it from the array. (Aside: you didn't say what happens if the maximum value appears more than once. See the code below and the last sentence.)
you don't need n=arr.length. for i in 1..arr.length is enough, except...
for ... is rarely used. Rubyists tend to use each instead, or one of the many methods from the Enumerable (mix-in) module (all of which invoke each).
you don't need return false because Ruby returns the value of the last statement evaluated, which is false if the method does not return true earlier.
Here's a more Ruby-like way to do that:
def max_match?(arr)
mx = arr.max
whats_left = arr - [mx]
(1..whats_left.size).any? {|n| whats_left.combination(n).any? {|c|
c.reduce(:+) == mx }}
end
arr = [1,4,7,3,5,2,13]
max_match?(arr) #=> true
arr = [1,3,4,7,59]
max_match?(arr) #=> false
If you wish this to return true when arr contains more than one value equal to mx, insert the following after mx = arr.max:
return true if arr.count(mx) > 1

Can't understand this method tutorial on RubyMonk

I'm currently going through the exercises on rubymonk.com, but just can't get my head around this problem.
I'm meant to write three methods, add, subtract and calculate. Calculate is a method combining the add and subtract methods depending on the last argument passed to the method, which should be a hash containing the symbol ':add' or ':subtract'
This is the solution code:
def add(*numbers)
numbers.inject(0) { |sum, number| sum + number }
end
def subtract(*numbers)
sum = numbers.shift
numbers.inject(sum) { |sum, number| sum - number }
end
def calculate(*arguments)
# if the last argument is a Hash, extract it
# otherwise create an empty Hash
options = arguments[-1].is_a?(Hash) ? arguments.pop : {}
options[:add] = true if options.empty?
return add(*arguments) if options[:add]
return subtract(*arguments) if options[:subtract]
end
I understand everything but this line:
options = arguments[-1].is_a?(Hash) ? arguments.pop : {}
Even then, I understand what is occurring, but I don't understand why there's a second '?' or why ': {}' is there. Could anyone explain to me what's going on here?
Thanks!
It's a ternary logic operator.
The format is:
<expression to evaluate> ? <result if expression> : <result if !expression>
So in your case, the operator is a shorthand for:
if arguments[-1].is_a?(Hash)
arguments.pop
else
{}
end
Also, {} is a constructor for an empty Hash object - this is a guard clause to ensure that options will not be nil after the assignment.
The comment actually explains it. A different way to write it is this:
if (arguments[-1].is_a?(Hash)) {
arguments.pop
}
else {
{}
}
Where arguments[-1] means the last argument.

Resources