Search code examples
rubyparametersmethods

Passing a method as a parameter in Ruby


I am trying to learn Ruby, and want to implement the Python algorithms from the book "Programming Collective Intelligence" in Ruby.

In chapter 8 the author passed a method as a parameter which seems to work in Python but not in Ruby.

I am using the method:

def gaussian(dist, sigma=10.0)
  foo
end

and want to call this using another method:

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

But I got is an error:

ArgumentError: wrong number of arguments (0 for 1)

Solution

  • 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.