J'ai une méthode à l'intérieur d'une méthode. La méthode intérieure dépend d'une boucle variable en cours d'exécution. C'est une mauvaise idée?
MISE À JOUR: Comme cette réponse semble avoir suscité un certain intérêt ces derniers temps, je voulais souligner qu'il y a discussion sur le Ruby tracker problème à supprimer la fonctionnalité discutée ici, à savoir interdire d'avoir des définitions de méthode dans un corps de méthode .
Non, Ruby n'a pas de méthodes imbriquées.
Vous pouvez faire quelque chose comme ça:
class Test1
def meth1
def meth2
puts "Yay"
end
meth2
end
end
Test1.new.meth1
Mais ce n'est pas une méthode imbriquée. Je répète: Ruby n'a pas de méthodes imbriquées.
Il s'agit d'une définition de méthode dynamique. Lorsque vous exécutez meth1
, le corps de meth1
sera exécuté. Le corps arrive juste à définir une méthode nommée meth2
, c'est pourquoi après avoir exécuté meth1
une fois, vous pouvez appeler meth2
.
Mais où est meth2
défini? Eh bien, ce n'est évidemment pas défini comme une méthode imbriquée, car il n'y a pas des méthodes imbriquées dans Ruby. Il est défini comme une méthode d'instance de Test1
:
Test1.new.meth2
# Yay
De plus, il sera évidemment redéfini à chaque fois que vous exécutez meth1
:
Test1.new.meth1
# Yay
Test1.new.meth1
# test1.rb:3: warning: method redefined; discarding old meth2
# test1.rb:3: warning: previous definition of meth2 was here
# Yay
En bref: non, Ruby ne prend pas en charge les méthodes imbriquées.
Notez également que dans Ruby, les corps de méthode ne peuvent pas être des fermetures, seuls les corps de bloc peuvent l'être. Cela élimine à peu près le cas d'utilisation majeur pour les méthodes imbriquées, car même si Ruby les méthodes imbriquées prises en charge, vous ne pourriez pas ' t utilisez les variables de la méthode externe dans la méthode imbriquée.
MISE À JOUR SUITE: à une étape plus tard , alors, cette syntaxe pourrait être réutilisée pour ajouter des méthodes imbriquées à Ruby, qui se comporterait comme je l'ai décrit: ils seraient limités à leur méthode de confinement, c'est-à-dire invisibles et inaccessibles en dehors de leur corps de méthode de confinement. Et éventuellement, ils auraient accès à la portée lexicale de leur méthode conteneur. Cependant, si vous lisez la discussion que j'ai liée ci-dessus, vous pouvez observer que matz est fortement contre les méthodes imbriquées (mais toujours pour supprimer les définitions de méthodes imbriquées).
En fait, c'est possible. Vous pouvez utiliser procs/lambda pour cela.
def test(value)
inner = ->() {
value * value
}
inner.call()
end
Non, non, Ruby a des méthodes imbriquées. Vérifiez ceci:
def outer_method(arg)
outer_variable = "y"
inner_method = lambda {
puts arg
puts outer_variable
}
inner_method[]
end
outer_method "x" # prints "x", "y"
Vous pouvez faire quelque chose comme ça
module Methods
define_method :outer do
outer_var = 1
define_method :inner do
puts "defining inner"
inner_var = outer_var +1
end
outer_var
end
extend self
end
Methods.outer
#=> defining inner
#=> 1
Methods.inner
#=> 2
Ceci est utile lorsque vous faites des choses comme l'écriture de DSL qui nécessitent le partage de la portée entre les méthodes. Mais sinon, vous feriez bien mieux de faire autre chose, car comme les autres réponses l'ont dit, inner
est redéfini chaque fois que outer
est invoqué. Si vous voulez ce comportement, et vous le pouvez parfois, c'est un bon moyen de l'obtenir.
La manière Ruby consiste à truquer avec des hacks déroutants qui amèneront certains utilisateurs à se demander "Comment ça marche même?", Tandis que les moins curieux mémoriseront simplement la syntaxe nécessaire pour utiliser le Si vous avez déjà utilisé Rake ou Rails, vous avez vu ce genre de chose.
Voici un tel hack:
def mlet(name,func)
my_class = (Class.new do
def initialize(name,func)
@name=name
@func=func
end
def method_missing(methname, *args)
puts "method_missing called on #{methname}"
if methname == @name
puts "Calling function #{@func}"
@func.call(*args)
else
raise NoMethodError.new "Undefined method `#{methname}' in mlet"
end
end
end)
yield my_class.new(name,func)
end
Cela permet de définir une méthode de niveau supérieur qui crée une classe et la transmet à un bloc. La classe utilise method_missing
Pour prétendre qu'elle a une méthode avec le nom que vous avez choisi. Il "implémente" la méthode en appelant le lambda que vous devez fournir. En nommant l'objet avec un nom à une lettre, vous pouvez minimiser la quantité de frappe supplémentaire qu'il nécessite (ce qui est la même chose que Rails fait dans son schema.rb
)). mlet
est nommé d'après la forme LISP commune flet
, sauf lorsque f
signifie "fonction", m
signifie "méthode".
Vous l'utilisez comme ceci:
def outer
mlet :inner, ->(x) { x*2 } do |c|
c.inner 12
end
end
Il est possible de créer un engin similaire qui permet de définir plusieurs fonctions internes sans imbrication supplémentaire, mais qui nécessite un hack encore plus laid du type que vous pourriez trouver dans l'implémentation de Rake ou Rspec. Comprendre le fonctionnement de let!
De Rspec vous permettrait de créer une horrible abomination.