Mais ce n'est pas une méthode imbriquée. Je le répète: Ruby n'a pas de méthodes imbriquées.
Ce que c'est, est une définition de méthode dynamique. Lorsque vous exécutez meth1, le corps de meth1sera exécuté. Le corps définit simplement une méthode nommée meth2, c'est pourquoi après avoir exécuté meth1une fois, vous pouvez appeler meth2.
Mais où est meth2défini? Eh bien, ce n'est évidemment pas défini comme une méthode imbriquée, car il n'y a pas de 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é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 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 le peuvent. Cela élimine à peu près le cas d'utilisation majeur des méthodes imbriquées, car même si Ruby supportait les méthodes imbriquées, vous ne pouviez pas utiliser les variables de la méthode externe dans la méthode imbriquée.
MISE À JOUR SUITE: à un stade ultérieur , cette syntaxe pourrait être réutilisée pour ajouter des méthodes imbriquées à Ruby, qui se comporteraient comme je l'ai décrit: elles seraient limitées à leur méthode contenant, c'est-à-dire invisibles et inaccessibles en dehors de leur méthode contenant corps. Et éventuellement, ils auraient accès à la portée lexicale de leur méthode contenante. 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).
Vous pouvez également montrer, cependant, comment créer un lambda de fermeture dans une méthode pour DRYness, ou pour exécuter la récursivité.
Phrogz
119
J'ai l'impression que Ruby n'a peut-être pas de méthodes imbriquées.
Mark Thomas
16
@Mark Thomas: Ai-je oublié de mentionner que Ruby n'a pas de méthodes imbriquées? :-) Sérieusement: au moment où je l' ai écrit cette réponse, il y avait déjà trois réponses, chacun de qui prétendait que Ruby n'ont des méthodes imbriquées. Certaines de ces réponses ont même eu des votes positifs, bien qu'elles soient manifestement fausses. L'une a même été acceptée par l'OP, encore une fois, malgré son erreur. L'extrait de code utilisé par cette réponse pour prouver que Ruby prend en charge les méthodes imbriquées prouve en fait le contraire, mais apparemment ni les votants positifs ni l'OP n'ont pris la peine de vérifier. Donc, j'ai donné une bonne réponse pour chaque mauvaise. :-)
Jörg W Mittag
10
Lorsque vous réalisez que ce ne sont que des instructions au noyau qui modifient les tables et que les méthodes, les classes et les modules ne sont que des entrées dans des tables et pas vraiment réelles, vous devenez comme Neo quand il voit à quoi ressemble la matrice. Alors vous pourriez vraiment devenir philosophique et dire qu'à part les méthodes imbriquées, il n'y a même pas de méthodes. Il n'y a même pas d'agents. Ce sont des programmes dans la matrice. Même ce steak juteux que vous mangez n'est qu'une entrée dans une table.
mydoghasworms
3
Il n'y a pas de méthodes, votre code n'est qu'une simulation dans The Matrix
bbozo
13
En fait, c'est possible. Vous pouvez utiliser procs / lambda pour cela.
deftest(value)
inner = ->() {
value * value
}
inner.call()
end
Vous n'avez pas tort, mais votre réponse est formulée comme une solution pour obtenir des méthodes imbriquées. Quand en réalité vous n'utilisez que des procs qui ne sont pas des méthodes. C'est une bonne réponse en dehors de prétendre résoudre des "méthodes imbriquées"
Brandon Buck
5
Non, non, Ruby a des méthodes imbriquées. Vérifie ça:
defouter_method(arg)
outer_variable = "y"
inner_method = lambda {
puts arg
puts outer_variable
}
inner_method[]
end
outer_method "x"# prints "x", "y"
inner_method n'est pas une méthode, c'est une fonction / lambda / proc. Il n'y a aucune instance associée à aucune classe, donc ce n'est pas une méthode.
Ceci est utile lorsque vous faites des choses comme l'écriture de DSL qui nécessitent le partage de portée entre les méthodes. Mais sinon, vous feriez bien mieux de faire autre chose, car comme le disent les autres réponses, il innerest redéfini chaque fois qu'il outerest invoqué. Si vous voulez ce comportement, et vous pourriez parfois le faire, c'est un bon moyen de l'obtenir.
La méthode Ruby est de le simuler avec des hacks déroutants qui amèneront certains utilisateurs à se demander «Comment ça marche, putain?», Tandis que les moins curieux mémoriseront simplement 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:
defmlet(name,func)
my_class = (Class.new dodefinitialize(name,func)
@name=name
@func=func
enddefmethod_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"endendend)
yield my_class.new(name,func)
end
Cela définit une méthode de niveau supérieur qui crée une classe et la transmet à un bloc. La classe utilise method_missingpour 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 d'une lettre, vous pouvez minimiser la quantité de saisie supplémentaire qu'il nécessite (ce qui est la même chose que Rails fait dans son schema.rb). mletest nommé d'après la forme Common Lisp flet, sauf où fsignifie «fonction»,m signifie «méthode».
Il est possible de créer un engin similaire qui permet de définir plusieurs fonctions internes sans imbrication supplémentaire, mais cela nécessite un hack encore plus laid du type que vous pourriez trouver dans l'implémentation de Rake ou de Rspec. Comprendre comment les let!travaux de Rspec vous aideraient à créer une abomination aussi horrible.
Réponses:
MISE À JOUR: Puisque cette réponse semble avoir suscité un certain intérêt ces derniers temps, je voulais souligner qu'il y a une discussion sur le suivi des problèmes Ruby pour 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 ceci:
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 le répète: Ruby n'a pas de méthodes imbriquées.
Ce que c'est, est une définition de méthode dynamique. Lorsque vous exécutez
meth1
, le corps demeth1
sera exécuté. Le corps définit simplement une méthode nomméemeth2
, c'est pourquoi après avoir exécutémeth1
une fois, vous pouvez appelermeth2
.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 de méthodes imbriquées dans Ruby. Il est défini comme une méthode d'instance deTest1
:Test1.new.meth2 # Yay
De plus, 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 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 le peuvent. Cela élimine à peu près le cas d'utilisation majeur des méthodes imbriquées, car même si Ruby supportait les méthodes imbriquées, vous ne pouviez pas utiliser les variables de la méthode externe dans la méthode imbriquée.
MISE À JOUR SUITE: à un stade ultérieur , cette syntaxe pourrait être réutilisée pour ajouter des méthodes imbriquées à Ruby, qui se comporteraient comme je l'ai décrit: elles seraient limitées à leur méthode contenant, c'est-à-dire invisibles et inaccessibles en dehors de leur méthode contenant corps. Et éventuellement, ils auraient accès à la portée lexicale de leur méthode contenante. 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).
la source
En fait, c'est possible. Vous pouvez utiliser procs / lambda pour cela.
def test(value) inner = ->() { value * value } inner.call() end
la source
Non, non, Ruby a des méthodes imbriquées. 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"
la source
Tu peux 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 portée entre les méthodes. Mais sinon, vous feriez bien mieux de faire autre chose, car comme le disent les autres réponses, il
inner
est redéfini chaque fois qu'ilouter
est invoqué. Si vous voulez ce comportement, et vous pourriez parfois le faire, c'est un bon moyen de l'obtenir.la source
La méthode Ruby est de le simuler avec des hacks déroutants qui amèneront certains utilisateurs à se demander «Comment ça marche, putain?», Tandis que les moins curieux mémoriseront simplement 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 définit 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 d'une lettre, vous pouvez minimiser la quantité de saisie supplémentaire qu'il nécessite (ce qui est la même chose que Rails fait dans sonschema.rb
).mlet
est nommé d'après la forme Common Lispflet
, 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 qui permet de définir plusieurs fonctions internes sans imbrication supplémentaire, mais cela nécessite un hack encore plus laid du type que vous pourriez trouver dans l'implémentation de Rake ou de Rspec. Comprendre comment les
let!
travaux de Rspec vous aideraient à créer une abomination aussi horrible.la source
:-RÉ
Ruby a des méthodes imbriquées, mais 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"
la source