Ist es möglich, Methoden in Methoden zu haben?

87

Ich habe eine Methode innerhalb einer Methode. Die innere Methode hängt von einer variablen Schleife ab, die ausgeführt wird. Ist das eine schlechte Idee?

Ausflug
quelle
2
Können Sie das Codebeispiel oder zumindest ein logisches Äquivalent zu dem, was Sie versuchen, freigeben?
Aaron Scruggs

Antworten:

164

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 meth1ausgeführt. Der Body definiert zufällig eine Methode mit dem Namen meth2, weshalb Sie nach meth1einmaliger Ausführung aufrufen können meth2.

Aber wo ist meth2definiert? 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).

Jörg W Mittag
quelle
6
Sie können jedoch auch zeigen, wie Sie ein Abschluss-Lambda in einer Methode für DRYness erstellen oder eine Rekursion ausführen.
Phrogz
119
Ich habe das Gefühl, dass Ruby möglicherweise keine verschachtelten Methoden hat.
Mark Thomas
16
@ Mark Thomas: Habe ich vergessen zu erwähnen, dass Ruby keine verschachtelten Methoden hat? :-) Im Ernst: ich zu der Zeit diese Antwort schrieb, gab es bereits drei Antworten, beansprucht jeder einzelne von denen , dass Ruby tut verschachtelte Methoden. Einige dieser Antworten hatten sogar positive Stimmen, obwohl sie offensichtlich falsch waren. Einer wurde sogar wieder vom OP akzeptiert, obwohl er falsch lag. Das Code-Snippet, mit dem diese Antwort beweist, dass Ruby verschachtelte Methoden unterstützt, beweist tatsächlich das Gegenteil, aber anscheinend haben sich weder die Upvoter noch das OP tatsächlich die Mühe gemacht, dies zu überprüfen. Also gab ich für jede falsche eine richtige Antwort. :-)
Jörg W Mittag
10
Wenn Sie feststellen, dass dies alles nur Anweisungen an den Kernel sind, die Tabellen ändern, und dass Methoden, Klassen und Module nur Einträge in Tabellen sind und nicht wirklich real, werden Sie wie Neo, wenn er sieht, wie die Matrix aussieht. Dann könnte man wirklich philosophisch werden und sagen, dass es neben verschachtelten Methoden nicht einmal Methoden gibt. Es gibt nicht einmal Agenten. Sie sind Programme in der Matrix. Sogar das saftige Steak, das Sie essen, ist nur ein Eintrag in einem Tisch.
Mydoghaswürmer
3
Es gibt keine Methoden, Ihr Code ist nur eine Simulation in The Matrix
bbozo
12

Eigentlich ist es möglich. Sie können hierfür procs / lambda verwenden.

def test(value)
  inner = ->() {
    value * value
  }
  inner.call()
end
SuperManEver
quelle
2
Sie liegen nicht falsch, aber Ihre Antwort ist eine Lösung, um verschachtelte Methoden zu erreichen. In Wirklichkeit verwenden Sie nur Procs, die keine Methoden sind. Es ist eine gute Antwort außerhalb der Behauptung, "verschachtelte Methoden" zu lösen
Brandon Buck
5

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"
iirekm
quelle
9
inner_method ist keine Methode, sondern eine Funktion / lambda / proc. Es gibt keine zugeordnete Instanz einer Klasse, daher handelt es sich nicht um eine Methode.
Sami Samhuri
2

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, innerbei jedem outerAufruf neu definiert wird. Wenn Sie dieses Verhalten möchten und manchmal auch möchten, ist dies ein guter Weg, um es zu bekommen.

mdmoskwa
quelle
2

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_missingvor, 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). mletist nach der Common Lisp-Form benannt flet, außer wo ffür "Funktion" msteht , 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.

Konto wegwerfen
quelle
-3

:-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" 
bbozo
quelle
4
Dies ist keine verschachtelte Methode ... siehe Jörg W Mittag Antwort für klares Verständnis.
Hardik