Передача метода в качестве параметра в Ruby
Я пытаюсь немного пошалить с Руби. Для этого я пытаюсь реализовать алгоритмы (приведенные в Python) из книги "Программирование коллективного разума" Ruby.
В главе 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)
8 ответов:
вы хотите объект 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в этом случае вы просто вызываете метод, который определен на объекте, а не передаете полный кусок кода. В зависимости от как вы структурируете это вам может понадобиться заменить
self.sendСobject_that_has_the_these_math_methods.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но похоже, что вы хотели бы более многоразовые куски кода здесь.
комментарии, относящиеся к блокам и процессам, правильны в том, что они более обычны в Ruby. Но вы можете передать метод, если хотите. Вы звоните
methodчтобы получить метод и.callназвать это:def weightedknn( data, vec1, k = 5, weightf = method(:gaussian) ) ... weight = weightf.call( dist ) ... end
вы можете передать метод в качестве параметра с
method(:function)путь. Ниже приведен очень простой пример: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
обычный рубиновый способ сделать это-использовать блок.
Так это будет что-то вроде:
def weightedknn( data, vec1, k = 5 ) foo weight = yield( dist ) foo endи используется как:
weightenknn( data, vec1 ) { |dist| gaussian( dist ) }этот шаблон широко используется в Ruby.
можно использовать
&оператор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 )EDIT: как объясняется в комментариях, этот подход неверен. Это будет работать, если вы используете Procs вместо обычных функций.
Я бы рекомендовал использовать амперсанд, чтобы иметь доступ к именованным блокам внутри функции. Следуя рекомендациям, приведенным в в этой статье вы можете написать что-то вроде этого (это настоящий лом из моей рабочей программы):
# 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 endAfterwerds, вы можете вызвать эту функцию следующим образом:
@contests = make_select_list Contest do |contest| logged_admin? or contest.organizer == @current_user endЕсли вам не нужно фильтровать ваш выбор, вы просто опустите блок:
@categories = make_select_list( Category ) # selects all categoriesтак много для власти рубиновых блоков.
вы также можете использовать "eval" и передать метод в качестве строкового аргумента, а затем просто оценить его в другом методе.
Comments