J'essaie de déconner un peu avec Ruby. Par conséquent, j'essaie d'implémenter les algorithmes (donnés en Python) du livre "Programming Collective Intelligence" de Ruby.
Au chapitre 8, l'auteur passe une méthode a en tant que paramètre. Cela semble fonctionner dans Python mais pas dans Ruby.
J'ai ici la méthode
def gaussian(dist, sigma=10.0)
foo
end
et veulent appeler cela avec une autre méthode
def weightedknn(data, vec1, k = 5, weightf = gaussian)
foo
weight = weightf(dist)
foo
end
Tout ce que j'ai c'est une erreur
ArgumentError: wrong number of arguments (0 for 1)
Vous voulez un objet 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
Notez simplement que vous ne pouvez pas définir un argument par défaut dans une déclaration de bloc comme celle-ci. Vous devez donc utiliser un splat et configurer la valeur par défaut dans le code proc lui-même.
Ou, selon votre portée, il peut être plus facile de passer un nom de méthode à la place.
def weightedknn(data, vec1, k = 5, weightf = :gaussian)
...
weight = self.send(weightf)
...
end
Dans ce cas, vous appelez simplement une méthode définie sur un objet plutôt que de transmettre un bloc de code complet. En fonction de votre structure, vous devrez peut-être remplacer self.send
avec object_that_has_the_these_math_methods.send
Dernier point mais non le moindre, vous pouvez suspendre un bloc de la méthode.
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
Mais il semblerait que vous souhaitiez davantage de morceaux de code réutilisables ici.
Les commentaires faisant référence aux blocs et Procs sont corrects en ce qu'ils sont plus habituels en Ruby. Mais vous pouvez passer une méthode si vous voulez. Vous appelez method
pour obtenir la méthode et .call
pour l'appeler:
def weightedknn( data, vec1, k = 5, weightf = method(:gaussian) )
...
weight = weightf.call( dist )
...
end
Vous pouvez passer une méthode en paramètre avec method(:function)
way. Voici un exemple très simple:
def double (a) renvoyer a * 2 end => nil def method_with_function_as_param (rappel, numéro) callback.call (number) end => nil method_with_function_as_param (method (: double), 10) => 20
La méthode normale Ruby) consiste à utiliser un bloc.
Donc, ce serait quelque chose comme:
def weightedknn( data, vec1, k = 5 )
foo
weight = yield( dist )
foo
end
Et utilisé comme:
weightenknn( data, vec1 ) { |dist| gaussian( dist ) }
Ce modèle est largement utilisé en Ruby.
Vous pouvez utiliser le &
opérateur sur l’instance Method
de votre méthode pour convertir la méthode en bloc .
Exemple:
def foo(arg)
p arg
end
def bar(&block)
p 'bar'
block.call('foo')
end
bar(&method(:foo))
Plus de détails sur http://weblog.raganwald.com/2008/06/what-does-do-when-used-as-unary.html
Je recommanderais d'utiliser esperluette pour avoir un accès aux blocs nommés dans une fonction. En suivant les recommandations données dans cet article , vous pouvez écrire quelque chose comme ceci (ceci est un véritable extrait de mon programme de travail):
# 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
Par la suite, vous pouvez appeler cette fonction comme suit:
@contests = make_select_list Contest do |contest|
logged_admin? or contest.organizer == @current_user
end
Si vous n'avez pas besoin de filtrer votre sélection, vous omettez simplement le bloc:
@categories = make_select_list( Category ) # selects all categories
Voilà pour le pouvoir de Ruby bloque.
Vous devez appeler la méthode "call" de l'objet function:
weight = weightf.call( dist )
EDIT: comme expliqué dans les commentaires, cette approche est fausse. Cela fonctionnerait si vous utilisez Procs au lieu de fonctions normales.