Passing a method as a parameter in Ruby

I'm trying to figure out a bit with Ruby. Therefore, I am trying to implement the algorithms (given in Python) from the book "Collective Intelligence Programming" by Ruby.

In chapter 8, the author passes method a as a parameter. This seems to work in Python, but not in Ruby.

I have a method

def gaussian(dist, sigma=10.0) foo end 

and want to call it another way.

 def weightedknn(data, vec1, k = 5, weightf = gaussian) foo weight = weightf(dist) foo end 

All I have is a mistake

 ArgumentError: wrong number of arguments (0 for 1) 
+105
methods ruby parameters
Feb 07 '09 at 0:02
source share
8 answers

You want a proc object:

 gaussian = Proc.new do |dist, *args| sigma = args.first || 10.0 ... end def weightedknn(data, vec1, k = 5, weightf = gaussian) ... weight = weightf.call(dist) ... end 

Just remember that you cannot set a default argument in a block declaration. Therefore, you need to use splat and set the default value in the proc code itself.




Or, depending on your scope, it might be easier to pass in place of the method name.

 def weightedknn(data, vec1, k = 5, weightf = :gaussian) ... weight = self.send(weightf) ... end 

In this case, you simply call the method that is defined on the object, and not pass the complete code snippet. Depending on how you structure it, you may need to replace self.send with object_that_has_the_these_math_methods.send




Last but not least, you can hang a block using the method.

 def weightedknn(data, vec1, k = 5) ... weight = if block_given? yield(dist) else gaussian.call(dist) end end ... end weightedknn(foo, bar) do |dist| # square the dist dist * dist end 

But it looks like you would like to use multiple code snippets here.

+88
Feb 07 '09 at 0:24
source share

The comments related to blocks and Procs are true in that they are more common in Ruby. But you can pass a method if you want. You call method to get the method and .call to call it:

 def weightedknn( data, vec1, k = 5, weightf = method(:gaussian) ) ... weight = weightf.call( dist ) ... end 
+92
Feb 07 '09 at 15:50
source share

You can pass a method as a parameter with method(:function) way. The following is a very simple example:

 def double (a)
   return a * 2 
 end
 => nil

 def method_with_function_as_param (callback, number) 
   callback.call (number) 
 end 
 => nil

 method_with_function_as_param (method (: double), 10) 
 => 20
+40
Nov 04 '10 at 8:39
source share

The usual way for Ruby to do this is to use a block.

So this will be something like:

 def weightedknn( data, vec1, k = 5 ) foo weight = yield( dist ) foo end 

And used as:

 weightenknn( data, vec1 ) { |dist| gaussian( dist ) } 

This pattern is widely used in Ruby.

+24
Feb 07 '09 at 0:41
source share

You can use the & operator in the Method instance of your method to convert the method into a block.

Example:

 def foo(arg) p arg end def bar(&block) p 'bar' block.call('foo') end bar(&method(:foo)) 

Read more at http://weblog.raganwald.com/2008/06/what-does-do-when-used-as-unary.html

+12
Dec 30 '14 at 11:04
source share

You must call the "call" method of the function object:

 weight = weightf.call( dist ) 

EDIT: as explained in the comments, this approach is wrong. This will work if you use Procs instead of the usual functions.

0
Feb 07 '09 at 0:06
source share

I would recommend using ampersand to access named blocks inside a function. Following the recommendations in this article , you can write something like this (this is a real departure from my work program):

  # Returns a valid hash for html form select element, combined of all entities # for the given +model+, where only id and name attributes are taken as # values and keys correspondingly. Provide block returning boolean if you # need to select only specific entities. # # * *Args* : # - +model+ -> ORM interface for specific entities' # - +&cond+ -> block {|x| boolean}, filtering entities upon iterations # * *Returns* : # - hash of {entity.id => entity.name} # def make_select_list( model, &cond ) cond ||= proc { true } # cond defaults to proc { true } # Entities filtered by cond, followed by filtration by (id, name) model.all.map do |x| cond.( x ) ? { x.id => x.name } : {} end.reduce Hash.new do |memo, e| memo.merge( e ) end end 

After that, you can call this function as follows:

 @contests = make_select_list Contest do |contest| logged_admin? or contest.organizer == @current_user end 

If you do not need to filter your selection, you simply omit the block:

 @categories = make_select_list( Category ) # selects all categories 

So much for the power of Ruby blocks.

0
May 31 '13 at 12:23
source share

you can also use "eval" and pass the method as a string argument, and then just evaluate it in another method.

-5
Mar 18 '15 at 6:11
source share



All Articles