user3630282
user3630282

Reputation:

Block with two parameters

I found this code by user Hirolau:

def sum_to_n?(a, n)
  a.combination(2).find{|x, y| x + y == n}
end

a = [1, 2, 3, 4, 5]
sum_to_n?(a, 9)  # => [4, 5]
sum_to_n?(a, 11) # => nil

How can I know when I can send two parameters to a predefined method like find? It's not clear to me because sometimes it doesn't work. Is this something that has been redefined?

Upvotes: 5

Views: 735

Answers (3)

sawa
sawa

Reputation: 168249

find does not take two parameters, it takes one. The reason the block in your example takes two parameters is because it is using destruction. The preceding code a.combination(2) gives an array of arrays of two elements, and find iterates over it. Each element (an array of two elements) is passed at a time to the block as its single parameter. However, when you write more parameters than there is, Ruby tries to adjust the parameters by destructing the array. The part:

find{|x, y| x + y == n}

is a shorthand for writing:

find{|(x, y)| x + y == n}

Upvotes: 3

AmitA
AmitA

Reputation: 3245

If you look at the documentation of Enumerable#find, you see that it accepts only one parameter to the block. The reason why you can send it two, is because Ruby conveniently lets you do this with blocks, based on it's "parallel assignment" structure:

[[1,2,3], [4,5,6]].each {|x,y,z| puts "#{x}#{y}#{z}"}
# 123
# 456

So basically, each yields an array element to the block, and because Ruby block syntax allows "expanding" array elements to their components by providing a list of arguments, it works.

You can find more tricks with block arguments here.

a.combination(2) results in an array of arrays, where each of the sub array consists of 2 elements. So:

a = [1,2,3,4]
a.combination(2)
# => [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]

As a result, you are sending one array like [1,2] to find's block, and Ruby performs the parallel assignment to assign 1 to x and 2 to y.

Also see this SO question, which brings other powerful examples of parallel assignment, such as this statement:

a,(b,(c,d)) = [1,[2,[3,4]]]

Upvotes: 5

Marcin Bilski
Marcin Bilski

Reputation: 611

The find function iterates over elements, it takes a single argument, in this case a block (which does take two arguments for a hash):

h = {foo: 5, bar: 6}
result = h.find {|k, v| k == :foo && v == 5}
puts result.inspect #=> [:foo, 5]

The block takes only one argument for arrays though unless you use destructuring.

Update: It seems that it is destructuring in this case.

Upvotes: 0

Related Questions