Est-il possible d’avoir des méthodes dans les méthodes?

J’ai une méthode à l’intérieur d’une méthode. La méthode interne dépend d’une boucle de variable en cours d’exécution. Est-ce une mauvaise idée?

MISE À JOUR: Étant donné que cette réponse semble avoir suscité un intérêt ces derniers temps, je voulais souligner qu’il existe une discussion sur le suivi des problèmes Ruby pour supprimer la fonctionnalité décrite ici, à savoir interdire les définitions de méthodes dans un corps de méthode .


Non, Ruby n’a pas de méthodes nestedes.

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 nestede. Je répète: Ruby n’a pas de méthodes nestedes.

Qu’est-ce que c’est, est une définition de méthode dynamic. 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 définie la meth2 ? Eh bien, ce n’est évidemment pas défini comme une méthode nestede, car il n’y a pas de méthodes nestedes dans Ruby. Il est défini comme une méthode d’instance de Test1 :

 Test1.new.meth2 # Yay 

En outre, il sera évidemment redéfini à chaque fois que vous exécuterez 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 supporte pas les méthodes nestedes.

Notez également que dans Ruby, les corps de méthodes ne peuvent pas être des fermetures, seuls les corps de blocs peuvent l’être. Cela élimine le cas d’utilisation majeur des méthodes nestedes, car même si Ruby prenait en charge les méthodes nestedes, vous ne pouviez pas utiliser les variables de la méthode externe dans la méthode nestede.


UPDATE CONTINUED: à un stade ultérieur , alors, cette syntaxe pourrait être réutilisée pour append des méthodes nestedes à Ruby, qui se comporteraient comme je l’ai décrit: elles seraient étendues à leur méthode contenant, c’est-à-dire invisible et inaccessible en dehors de leur méthode corps. Et probablement, ils auraient access à la scope lexicale de leur méthode contenant. Cependant, si vous lisez la discussion que j’ai liée ci-dessus, vous pouvez observer que matz est fortement opposé aux méthodes nestedes (mais toujours pour supprimer les définitions de méthodes nestedes).

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 nestedes. Vérifie ça:

 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 écrire des DSL qui nécessitent un partage de scope entre les méthodes. Mais sinon, vous feriez mieux de faire autre chose, car, comme les autres réponses l’ont dit, l’ inner est redéfini chaque fois que l’on invoque l’ outer . Si vous voulez ce comportement et que vous le pouvez parfois, c’est un bon moyen de l’obtenir.

La manière Ruby est de faire semblant avec des hacks déroutants qui amèneront certains utilisateurs à se demander “Comment ça marche même?”, Tandis que les moins curieux vont simplement mémoriser la syntaxe nécessaire pour utiliser la chose. 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’object avec un nom à une lettre, vous pouvez réduire la quantité de frappe supplémentaire requirejse (ce qui est la même chose que Rails dans son schema.rb ). mlet est nommé d’après le flet forme Common Lisp, sauf où 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 permettant de définir plusieurs fonctions internes sans imbrication supplémentaire, mais cela nécessite un hack encore plus moche que celui que vous pourriez trouver dans l’implémentation de Rake ou de Rspec. Comprendre comment Rspec a let! les œuvres vous aideraient à créer une abomination si horrible.

:-RÉ

Ruby a des méthodes nestedes, seulement elles ne font pas ce que vous attendez d’elles

 1.9.3p484 :001 > def kme; 'kme'; def foo; 'foo'; end; end => nil 1.9.3p484 :003 > self.methods.include? :kme => true 1.9.3p484 :004 > self.methods.include? :foo => false 1.9.3p484 :005 > kme => nil 1.9.3p484 :006 > self.methods.include? :foo => true 1.9.3p484 :007 > foo => "foo"