Passing a method as a parameter in Ruby
我试图用Ruby搞砸一下。 因此,我尝试从"Programming Collective Intelligence"一书中实现算法(在Python中给出)。
在第8章中,作者将方法a作为参数传递。 这似乎适用于Python,但不适用于Ruby。
我有这个方法
1 2 3 | def gaussian(dist, sigma=10.0) foo end |
并希望用另一种方法调用它
1 2 3 4 5 | def weightedknn(data, vec1, k = 5, weightf = gaussian) foo weight = weightf(dist) foo end |
我得到的只是一个错误
1 | ArgumentError: wrong number of arguments (0 for 1) |
关于块和过程的注释是正确的,因为它们在Ruby中更常见。但是如果你愿意,你可以传递一个方法。你调用
1 2 3 4 5 | def weightedknn( data, vec1, k = 5, weightf = method(:gaussian) ) ... weight = weightf.call( dist ) ... end |
你想要一个proc对象:
1 2 3 4 5 6 7 8 9 10 | 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代码中设置默认值。
或者,根据您所有这些的范围,可能更容易传递方法名称。
1 2 3 4 5 | def weightedknn(data, vec1, k = 5, weightf = :gaussian) ... weight = self.send(weightf) ... end |
在这种情况下,您只是调用在对象上定义的方法,而不是传递完整的代码块。根据您的结构,您可能需要将
最后但并非最不重要的,你可以挂掉方法的块。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 |
但听起来你想要更多可重复使用的代码块。
您可以使用
1 2 3 4 5 6 7 8 9 10 11 12 | 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 |
正常的Ruby方法是使用块。
所以它会是这样的:
1 2 3 4 5 | def weightedknn( data, vec1, k = 5 ) foo weight = yield( dist ) foo end |
使用如下:
1 | weightenknn( data, vec1 ) { |dist| gaussian( dist ) } |
这种模式在Ruby中广泛使用。
您可以在方法的
例:
1 2 3 4 5 6 7 8 9 10 | 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
我建议使用&符号来访问函数中的命名块。按照本文中给出的建议,您可以编写类似这样的内容(这是我工作程序的真正废料):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # 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,你可以像这样调用这个函数:
1 2 3 | @contests = make_select_list Contest do |contest| logged_admin? or contest.organizer == @current_user end |
如果您不需要过滤选择,则只需省略该块:
1 | @categories = make_select_list( Category ) # selects all categories |
对于Ruby块的强大功能如此之多。
你必须调用函数对象的方法"call":
1 | weight = weightf.call( dist ) |
编辑:正如评论中所解释的,这种方法是错误的。如果您使用Procs而不是普通函数,它将起作用。
你也可以使用"eval",并将该方法作为字符串参数传递,然后在其他方法中简单地对其进行评估。