Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing a method as a parameter in Ruby

People also ask

How do you pass a method as a parameter in Ruby?

The symbol terminology is Ruby's built-in way to allow you to reference a function without calling it. By placing the symbol in the argument for receives_function, we are able to pass all the info along and actually get into the receives_function code block before executing anything.

What is the name of the parameter passing method used in Ruby?

In ruby, arguments inside a method are passed by referencex. upcase!

What happens when you call a method in Ruby?

In ruby, the concept of object orientation takes its roots from Smalltalk. Basically, when you call a method, you are sending that object a message. So, it makes sense that when you want to dynamically call a method on an object, the method you call is send .


The comments referring to blocks and Procs are correct in that they are more usual 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

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 note that you can't set a default argument in a block declaration like that. So you need to use a splat and setup the default in the proc code itself.


Or, depending on your scope of all this, it may be easier to pass in a method name instead.

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

In this case you are just calling a method that is defined on an object rather than passing in a complete chunk of code. Depending on how you structure this you may need replace self.send with object_that_has_the_these_math_methods.send


Last but not least, you can hang a block off 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 sounds like you would like more reusable chunks of code here.


You can pass a method as parameter with method(:function) way. Below 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

The normal Ruby way to do this is to use a block.

So it would be something like:

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

And used like:

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

This pattern is used extensively in Ruby.


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

Example:

def foo(arg)
  p arg
end

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

bar(&method(:foo))

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


You have to call the method "call" of the function object:

weight = weightf.call( dist )

EDIT: as explained in the comments, this approach is wrong. It would work if you're using Procs instead of normal functions.


I would recommend to use ampersand to have an access to named blocks within a function. Following the recommendations given in this article you can write something like this (this is a real scrap from my working 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

Afterwerds, you can call this function like this:

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

If you don't 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.