Symbol#to_proc with custom methods - ruby

I like how in Ruby you can pass methods as blocks like so using Symbol#to_proc:
[1.0, 2.0, 3.0].map(&:to_i)
#=> [1, 2, 3]
I can also define my own lambda, times_two, and pass it as a block as well:
times_two = ->(x) {x * 2}
[1, 2, 3].map(&times_two)
#=> [2, 4, 6]
Though I seemingly cannot pass times_two as a symbol:
[1, 2, 3].map(&:times_two)
#=> ArgumentError: wrong number of arguments (0 for 1)
However when I try to do the same with a method I get an error:
def times_three(x)
x * 3
end
[1, 2, 3].map(&times_three)
#=> ArgumentError: wrong number of arguments (0 for 1)
[1, 2, 3].map(&:times_three)
#=> ArgumentError: wrong number of arguments (0 for 1)
I'm guessing I can't do this because times_three is a method, not a Proc.
So how can you define custom methods so that they can be used in the Symbol#to_proc fashion like to_i in the first example above?
For example, how can I do this?
[1, 2, 3].map(&:times_three)
#=> [3, 6, 9]
EDIT:
I watched the video posted below and apparently you can get close to Symbol#to_proc using the method method:
def times_three(x)
x * 3
end
t_three = method(:times_three)
[1, 2, 3].map(&t_three)
#=> [3, 6, 9]
However, it's not quite Symbol#to_proc:
[1, 2, 3].map(&:t_three)
#=> NoMethodError: undefined method `t_three' for 1:FixNum

class Integer
def times_three
return self * 3
end
end
Now, because times_three is now a method of the Integer class, you can do symbol to proc...
[1, 2, 3].map(&:times_three)
If you want to access a method that isn't part of the object's class but acts on an object, you need to pass the object as an argument to the method...
def times_three(x)
x * 3
end
[1, 2, 3].map{|i| times_three(i) }
the symbol to proc needs to use the object as a receiver.
[1, 2, 3].map(&:some_action)
is equivalent to
[1, 2, 3].map{|i| i.some_action}

You would have to define times_three on Integer or Numeric.
Symbol to Proc explained by Peter Cooper: https://www.youtube.com/watch?v=aISNtCAZlMg

Related

Ruby inject daisy chaining?

I'm not sure what sugar syntax this is, but let me just show you the problem.
def factors num
(1..num).select {|n| num % n == 0}
end
def mutual_factors(*nums)
nums
.map { |n| factors(n) }
.inject(:&)
end
p mutual_factors(50, 30) # [1, 2, 5, 10]
p mutual_factors(50, 30, 45, 105) # [1, 5]
p mutual_factors(8, 4) # [1, 2, 4]
p mutual_factors(8, 4, 10) # [1, 2]
p mutual_factors(12, 24) # [1, 2, 3, 4, 6, 12]
p mutual_factors(12, 24, 64) # [1, 2, 4]
p mutual_factors(22, 44) # [1, 2, 11, 22]
p mutual_factors(22, 44, 11) # [1, 11]
p mutual_factors(7) # [1, 7]
p mutual_factors(7, 9) # [1]
with this being the portion in questioning:
nums
.map { |n| factors(n) }
.inject(:&)
okay, so this is my mental trace: first, map uses the helper method to get the factors, and outputs the factors into another array, and then that array gets injected?
I think the
.inject(:&)
is what is throwing me off. I ran a quick google on it, but I haven't used inject for many things other than summing arrays, and basic stuff like that. I've also done things like
test = "hello".split("").map(&:upcase)
p test.join
but .inject(:&)? I know & is a proc, but I've only used them in arguments. I don't know the fundamentals under the hood. Please, take my current level into mind when trying to explain this to me =), I know how the basic inject works, and the splat operator also.
Partial quote form the documentation of Enumerable#inject.
inject(symbol) → object
[...]
Returns an object formed from operands via either:
A method named by symbol.
[...]
With method-name argument symbol, combines operands using the method:
# Sum, without initial_operand.
(1..4).inject(:+) # => 10
That means in the context of inject the (:&) is not a proc but simply the symbol :& that tells inject what operation to perform to combine the elements in the array.
Let's look at this example:
mutual_factors(8, 4, 10)
#=> [1, 2]
and let's look what happens at each step:
nums
.map { |n| factors(n) } #=> [[1, 2, 4, 8], [1, 2, 4], [1, 2, 5, 10]]
.inject(:&) #=> [1, 2, 4, 8] & [1, 2, 4] & [1, 2, 5, 10]
And Array#& is a method that returns a new array containing each element found in both arrays (duplicates are omitted).

Why a new call of a method with exclamation mark affects all previous calls of that method?

I'm sorry if this is a duplicate - I couldn't find anything similar in the existing posts.
I understand the difference between methods like shuffle and shuffle!. However, I am confused why calling the method more than once would result in changing the variables of all objects that previously referred to it? I'd expect once we apply a method, that the variable gets a value and we're done with it. Not that it continues to refer to the method call and the argument passed and that it would get re-evaluated later on.
I thought it's best to demonstrate with an example:
irb(main):001:1* def shuffle(arr)
irb(main):002:1* arr.shuffle!
irb(main):003:0> end
=> :shuffle
irb(main):004:0> arr = [1,2,3,4]
=> [1, 2, 3, 4]
irb(main):005:0> one = shuffle(arr)
=> [4, 2, 3, 1]
irb(main):006:0> two = shuffle(arr)
=> [1, 2, 4, 3]
irb(main):007:0> one
=> [1, 2, 4, 3]
So, here I'd expect one to stay [4, 2, 3, 1]. However, with each new call, all previous ones would get equated to the latest result of the method call. I realise it should have something to do with calling it with the same argument arr, but still doesn't quite make sense.
Array#shuffle! shuffles the array in-place and returns its receiver:
ary = [1, 2, 3, 4]
ary.equal?(ary.shuffle!) #=> true
Assigning the result from shuffle! to another variable doesn't change this. It merely results in two variables referring to the same array:
a = [1, 2, 3, 4]
b = a.shuffle!
a #=> [2, 4, 1, 3]
b #=> [2, 4, 1, 3]
a.equal?(b) #=> true
You probably want a new array. That's what Array#shuffle (without !) is for:
a = [1, 2, 3, 4]
b = a.shuffle
a #=> [1, 2, 3, 4]
b #=> [2, 4, 1, 3]
Even if shuffle returns the element in the original order, you'll get another array instance:
a = [1, 2, 3, 4]
b = a.shuffle until b == a
a #=> [1, 2, 3, 4]
b #=> [1, 2, 3, 4]
a.equal?(b) #=> false

How to use a recursive array

I have array, named a and define it with [1, 2, 3].
Next, I pushed it to itself:
a = [1, 2, 3]
a << a
and the result I get is:
#=> [1, 2, 3, [...]]
When I want to get the last element of array using a.last I get:
a.last
#=> [1, 2, 3, [...]]
#even
a.last.last.last
#=> [1, 2, 3, [...]]
What is going on, when we would push array to itself?
Yes, I understand that this should create a recursive array, but what can we do with it?
In Ruby variables, array elements etc. are object references. So when you do a = [1, 2, 3], there will be an array somewhere in memory and the a variable is a reference to that memory. Now when you do a << a, a[4] will also be a reference to that object. So in effect a now contains a reference to itself.
a = [1, 2, 3]
a << a.dup
a.last
=> [1, 2, 3]
a.last.last
=> 3
Maybe this is what you wanted. This just insert an array [1, 2, 3] as the last item of the a array. In the way you did you put a reference at the end of the a array and this becomes recursive.

Ruby Error: Array can't be coerced into Fixnum

I'm currently working on a simple multiplying method.
CODE:
def multiply(*numbers)
product = 1
numbers.each{|number|
product *= number
}
return product
end
puts multiply([2, 3, 4, 5])
OUTPUT:
*': Array can't be coerced into Fixnum (TypeError)
from calculator.rb:26:inblock in multiply'
from calculator.rb:24:in each'
from calculator.rb:24:inmultiply'
from calculator.rb:31:in `'
I get this error. It seems the method isn't allowing me to use ".each" on the array.
Also, I want to keep the parameter as *numbers in case it's not an array but two numbers to multiply. I should bring it to your attention that the method works fine when the parameter being passed are two numbers and not an array (i.e. multiply(2, 4)
multiply expects an arbitrary number of parameters. You pass only one parameter, which is an array. On the first iteration, number is the whole array. Hence the error message.
You have to fix the call, either
multiply(*[2, 3, 4, 5])
or, simpler,
multiply(2, 3, 4, 5)
The problem is different. In numbers.each you iterate over list of arguments, however with multiply([2, 3, 4, 5]) you are passing one argument, which is an array. The list of arguments is [[2, 3, 4, 5]]. So in the only iteration, you are trying to do:
1 *= [2, 3, 4, 5]
This basically makes no sense to Ruby, so it throws an error.
You should call multiply method with a list of arguments, not ona array argument:
multiply(2, 3, 4, 5)
Then it will work.
If you want to be able to use array as input, you can use to flatten method on the numbers array.
def multiply(*numbers)
product = 1
numbers.flatten.each{|number|
product *= number
}
return product
end
puts multiply([2, 3, 4, 5]) #=> 120
The flatten method will take a 2d array and turn into a simple array.
This will also work
multiply([2, 3, 4, 5], 2, 2) #=> 480
In this case,
numbers = [[2, 3, 4, 5], 2, 2]
After you apply the flatten method, you get
[2, 3, 4, 5, 2, 2]
Then you can begin to multiply each number.
As it is normal in Ruby, there is usually a method that do what you are looking to do. In this case, inject, and reduce can accomplish what you are looking for.
def multiply(*numbers)
numbers.flatten.reduce(&:*)
end
Nevermind! I realized that when passing an array using *args, it becomes and array of arrays.

Number of arguments in a ruby block

I'm new to Ruby, but not to languages that allow lambda's, such as groovy. So I saw this example:
myArray.product(otherArray).reject{|i,j| i > j}
in a ruby code block, and I hadn't seen this block take 2 arguments before, but when I went to look at the documentation I can only see the documentation that says that it takes 1 argument. I looked at the same for the enumerable class, but that doc only shows 1 argument also.
I understand that it works, I guess I was hoping that there was an easier way to determine how many arguments it takes other then a guess and test method. How can I tell how many arguments a block takes in Ruby?
This works because Ruby supports destructuring.
Destructuring allows you to bind a set of variables to a corresponding set of values anywhere that you can normally bind a value to a single variable.
This allows the following to hold true:
arr = [1, 2]
x = arr
x == [1, 2] # true
y, z = arr
y == 1 # true
z == 2 # true
You can see from the following code that destructuring in arguments to blocks isn't unique to the built-in methods that take a block:
def my_method(arr)
yield arr
end
my_method([1, 2, 3]) {|x| puts x.inspect }
# => [1, 2, 3]
my_method([1, 2, 3]) {|x, y, z| puts x.inspect }
# => 1
Check out Destructuring with Ruby for more information.
You can do some interesting restructuring in block parameters, depending on the structure of your array:
[[1, 2], [3, 4], [5, 6], [7, 8]].reject {|x,y| y == 8 }
#=> [[1, 2], [3, 4], [5, 6]]
You can group them in parentheses:
[ [[1,2],3], [[1,3],6] ].select {|(x,y),z| x == 1 && z == 3 }
#=> [ [[1,2],3] ]
You can also use the splat operator for various things, like dealing with variable-length subarrays:
[[:a,:b,2,3,4,5,6], [:c,:d,7,8,9]].each {|x,y,*numbers| puts numbers.inspect }
#=> [2,3,4,5,6]
#=> [7,8,9]
Ruby is flexible in how it interprets the arguments; here is a similar example, with one and then two arguments:
[1, 3].product([2, 4]).reject {|a| a.first > a.last }
=> [[1, 2], [1, 4], [3, 4]]
[1, 3].product([2, 4]).reject {|a,b| a > b }
=> [[1, 2], [1, 4], [3, 4]]
The rule of thumb here is that you can treat the arguments either as a composite object, or as individual elements in a collection. E.g.,
[1, 2, 3].tap {|a,b,c| puts [a,b,c].inspect }
[1, 2, 3]
...
[1, 2, 3].tap {|a,b| puts [a,b].inspect }
[1, 2]
...
[1, 2, 3].tap {|a| puts a.inspect }
[1, 2, 3]

Resources