Antworten:
UPDATE: Da diese Antwort in letzter Zeit ein gewisses Interesse gefunden zu haben scheint, wollte ich darauf hinweisen, dass es eine Diskussion über den Ruby Issue Tracker gibt, um die hier diskutierte Funktion zu entfernen , nämlich zu verbieten , Methodendefinitionen in einem Methodenkörper zu haben .
Nein, Ruby hat keine verschachtelten Methoden.
Sie können so etwas tun:
class Test1
def meth1
def meth2
puts "Yay"
end
meth2
end
end
Test1.new.meth1
Dies ist jedoch keine verschachtelte Methode. Ich wiederhole: Ruby hat keine verschachtelten Methoden.
Was dies ist, ist eine dynamische Methodendefinition. Wenn Sie laufen meth1
, wird der Body von meth1
ausgeführt. Der Body definiert zufällig eine Methode mit dem Namen meth2
, weshalb Sie nach meth1
einmaliger Ausführung aufrufen können meth2
.
Aber wo ist meth2
definiert? Nun, es ist offensichtlich nicht als verschachtelte Methode definiert, da es in Ruby keine verschachtelten Methoden gibt. Es ist definiert als eine Instanzmethode von Test1
:
Test1.new.meth2
# Yay
Außerdem wird es natürlich jedes Mal neu definiert, wenn Sie Folgendes ausführen 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
Kurz gesagt: Nein, Ruby unterstützt keine verschachtelten Methoden.
Beachten Sie auch, dass in Ruby Methodenkörper keine Abschlüsse sein können, sondern nur Blockkörper. Dadurch entfällt der Hauptanwendungsfall für verschachtelte Methoden, da selbst wenn Ruby verschachtelte Methoden unterstützt, Sie die Variablen der äußeren Methode nicht in der verschachtelten Methode verwenden können.
UPDATE FORTSETZUNG: Zu einem späteren Zeitpunkt könnte diese Syntax dann erneut zum Hinzufügen verschachtelter Methoden zu Ruby verwendet werden. Dies würde sich so verhalten, wie ich es beschrieben habe: Sie würden auf ihre enthaltende Methode beschränkt sein, dh außerhalb ihrer enthaltenden Methode unsichtbar und unzugänglich Körper. Und möglicherweise hätten sie Zugriff auf den lexikalischen Bereich ihrer enthaltenen Methode. Wenn Sie jedoch die Diskussion lesen, die ich oben verlinkt habe, können Sie feststellen, dass matz stark gegen verschachtelte Methoden ist (aber immer noch zum Entfernen verschachtelter Methodendefinitionen).
Eigentlich ist es möglich. Sie können hierfür procs / lambda verwenden.
def test(value)
inner = ->() {
value * value
}
inner.call()
end
Nein, nein, Ruby hat verschachtelte Methoden. Überprüfen Sie dies:
def outer_method(arg)
outer_variable = "y"
inner_method = lambda {
puts arg
puts outer_variable
}
inner_method[]
end
outer_method "x" # prints "x", "y"
Sie können so etwas tun
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
Dies ist nützlich, wenn Sie beispielsweise DSLs schreiben, bei denen der Gültigkeitsbereich zwischen den Methoden geteilt werden muss. Ansonsten ist es viel besser, etwas anderes zu tun, da es, wie in den anderen Antworten angegeben, inner
bei jedem outer
Aufruf neu definiert wird. Wenn Sie dieses Verhalten möchten und manchmal auch möchten, ist dies ein guter Weg, um es zu bekommen.
Der Ruby-Weg besteht darin, es mit verwirrenden Hacks zu fälschen, bei denen sich einige Benutzer fragen: "Wie zum Teufel funktioniert das überhaupt?", Während die weniger Neugierigen sich einfach die Syntax merken, die für die Verwendung des Dings erforderlich ist. Wenn Sie jemals Rake oder Rails verwendet haben, haben Sie so etwas gesehen.
Hier ist so ein 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
Damit wird eine Methode der obersten Ebene definiert, die eine Klasse erstellt und an einen Block übergibt. Die Klasse gibt method_missing
vor, eine Methode mit dem von Ihnen gewählten Namen zu haben. Es "implementiert" die Methode, indem es das Lambda aufruft, das Sie bereitstellen müssen. Indem Sie das Objekt mit einem Namen aus einem Buchstaben benennen, können Sie den Umfang der zusätzlichen Eingabe minimieren, die erforderlich ist (was auch bei Rails der Fall ist schema.rb
). mlet
ist nach der Common Lisp-Form benannt flet
, außer wo f
für "Funktion" m
steht , steht für "Methode".
Sie verwenden es so:
def outer
mlet :inner, ->(x) { x*2 } do |c|
c.inner 12
end
end
Es ist möglich, eine ähnliche Vorrichtung zu erstellen, mit der mehrere innere Funktionen ohne zusätzliche Verschachtelung definiert werden können, für die jedoch ein noch hässlicherer Hack erforderlich ist, wie Sie ihn möglicherweise in der Implementierung von Rake oder Rspec finden. Wenn Sie herausfinden, wie Rspecs let!
Arbeiten Sie dazu bringen, einen so schrecklichen Gräuel zu erschaffen.
:-D
Ruby hat verschachtelte Methoden, nur dass sie nicht das tun, was Sie von ihnen erwarten würden
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"