Rich_F
Rich_F

Reputation: 2056

Ruby Proc with Parameter

I'm trying to send a parameter to a Ruby proc

p1 = [54, 21, 45, 76, 12, 11, 67, 5]

qualify = proc { |age, other| age > other }

puts p1.select(&qualify(30))

This is the error I get:

undefined method `qualify' for main:Object

age comes from the iteration of the array, and I want to have that last parameter (30) to get into the proc.

Is a proc the right tool to be using for this? I'm new to proc. I'm unclear how to get that parameter in there.

Upvotes: 4

Views: 3727

Answers (3)

tadman
tadman

Reputation: 211590

The easy way is to shuffle up how you define this:

p1 = [54, 21, 45, 76, 12, 11, 67, 5]

qualify = proc { |age| age > 30 }

puts p1.select(&qualify).join(',')

By moving the 30 into the qualify proc you've baked in the condition, it's no longer dynamic. Remember, the only methods that can be used with the shorthand &: trick are zero-argument ones, or single argument ones with & on a proc.

You could also use a closure to have the comparison variable exposed:

p1 = [54, 21, 45, 76, 12, 11, 67, 5]

required = 30

qualify = proc { |age| age > required }

puts p1.select(&qualify).join(',')

required = 10

puts p1.select(&qualify).join(',')

The better way is to just spell it out, that's what Ruby is all about. Here in a more idiomatic form:

p1 = [54, 21, 45, 76, 12, 11, 67, 5]

puts p1.select { |age| age > 30 }

The only reason for an intermediate Proc is if you'd want to, for some reason, save that somewhere and re-use it later.

Upvotes: 4

Dyaniyal Wilson
Dyaniyal Wilson

Reputation: 1060

Use the select statement in the proc itself, so that the proc would calculate and return an array.

2.1.5 :119 >   qualify = proc { |age_array, age_limit| age_array.select { |age| age > age_limit } }
 => #<Proc:0xe7bc2cc@(irb):119> 
2.1.5 :120 > 
2.1.5 :121 >   
2.1.5 :122 >   qualify.call(p1, 30)
 => [54, 45, 76, 67] 

Upvotes: 3

mrzasa
mrzasa

Reputation: 23327

In order to use qualify in as select predicate, you need to reduce its arity (number of accepted arguments) through partial application. In other words - you need a new proc that would have other set to 30. It can be done with Method#curry, but it requires changing order of parameters:

qualify = proc { |other, age| age > other }
qualify.curry.call(30).call(10)
# => false
qualify.curry.call(30).call(40)
#=> true

I order to be able to pass this proc to select using &, you need to assign it so that it's available in the main object, e.g. by assigning it to an instance variable:

@qualify_30 = qualify.curry.call(30)

Now you can call:

p1.select{ |age| @qualify_30.call(age)  }
# => [54, 45, 76, 67]

or:

p1.select(&@qualify_30)
# => [54, 45, 76, 67]

or inline:

p1.select(&qualify.curry.call(30))
# => [54, 45, 76, 67]

Upvotes: 4

Related Questions