在Ruby中传递一个方法作为参数

我正在试图用Ruby来解决一些问题。 为此,我尝试从“Programming Collective Intelligence”Ruby这本书中实现这些algorithm(Python中给出的)。

在第8章中,作者通过一个方法a作为参数。 这似乎在Python中工作,但不是在Ruby中。

我在这里有这个方法

def gaussian(dist, sigma=10.0) foo end 

并想用另一种方法调用此方法

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

我得到的只是一个错误

 ArgumentError: wrong number of arguments (0 for 1) 

你想要一个proc对象:

 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 

只要注意,你不能像这样的块声明中设置一个默认的参数。 所以你需要使用splat并在proc代码中设置默认值。


或者,根据所有这些的范围,可能会更容易传入方法名称。

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

在这种情况下,您只是调用在对象上定义的方法,而不是传入完整的代码块。 根据你的结构,你可能需要用object_that_has_the_these_math_methods.sendreplaceself.send


最后但并非最不重要的是,您可以挂掉方法。

 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 

但是,这听起来像你想在这里更多的可重用的代码块。

引用块和Proc的注释是正确的,因为它们在Ruby中更常见。 但是如果你愿意,你可以传递一个方法。 你调用method来获取方法和.call来调用它:

 def weightedknn( data, vec1, k = 5, weightf = method(:gaussian) ) ... weight = weightf.call( dist ) ... end 

你可以通过method(:function)方式传递一个方法作为参数。 贝娄一个非常简单的例子:

 def double(a)
  返回一个* 2 
结束
 =>零

 def method_with_function_as_param(callback,number) 
   callback.call(数字) 
结束 
 =>零

 method_with_function_as_param(方法(:double),10) 
 => 20

正常的Ruby方法是使用块。

所以它会是这样的:

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

并用于:

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

这种模式在Ruby中被广泛使用。

您可以在Method实例上使用&运算符将该Method 转换为块

例:

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

有关详情, 请访问http://weblog.raganwald.com/2008/06/what-does-do-when-used-as-unary.html

你必须调用函数对象的方法“调用”:

 weight = weightf.call( dist ) 

编辑:正如评论中所解释的,这种做法是错误的。 这将工作,如果你使用Procs而不是普通的function。

我build议使用&符号来访问函数中的命名块。 按照这篇文章给出的build议,你可以写这样的东西(这是我的工作程序中的一个真正的废品):

  # 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 

AfterWork,你可以这样调用这个函数:

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

如果你不需要过滤你的select,你只需要忽略这个块:

 @categories = make_select_list( Category ) # selects all categories 

Ruby块的力量非常重要。

您也可以使用“eval”,并将该方法作为stringparameter passing,然后在另一个方法中简单地对其进行评估。