2011-02-01 13 views

Odpowiedz

135

AKTUALIZACJA: Ponieważ ta odpowiedź ostatnio wzbudziła pewne zainteresowanie, chciałem zauważyć, że istnieje discussion on the Ruby issue tracker to remove the feature discussed here, namely to forbid having method definitions inside a method body.


Nie, Ruby nie ma metod zagnieżdżonych.

można zrobić coś takiego:

class Test1 
    def meth1 
    def meth2 
     puts "Yay" 
    end 
    meth2 
    end 
end 

Test1.new.meth1 

Ale to nie zagnieżdżony metoda. Powtarzam: Ruby nie ma zagnieżdżonych metod.

Co to jest, jest definicją metody dynamicznej. Po uruchomieniu meth1 zostanie wykonane ciało meth1. Ciało po prostu zdarza się zdefiniować metodę o nazwie meth2, dlatego po uruchomieniu meth1 raz, możesz zadzwonić pod numer meth2.

Ale gdzie jest zdefiniowane meth2? Cóż, jest to oczywiście nie zdefiniowane jako metoda zagnieżdżona, ponieważ tam nie są to metody zagnieżdżone w Ruby. Jest zdefiniowana jako metoda instancji Test1:

Test1.new.meth2 
# Yay 

Także, to będzie oczywiście na nowo przy każdym uruchomieniu 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 

W skrócie: nie, Ruby nie wsparcia zagnieżdżone metod.

Należy również zauważyć, że w języku Ruby obiekty nie mogą być zamykane, tylko bryły bloków mogą. To praktycznie eliminuje główny przypadek użycia dla metod zagnieżdżonych, ponieważ nawet zagnieżdżone metody wspierane przez Ruby nie mogą używać zmiennych metody zewnętrznej w metodzie zagnieżdżonej.


UPDATE dalej: w później etapie, a następnie, składnia może być ponownie użyty do dodawania metodami łączenia rubinu, które zachowują się w ten sposób, że opisany sposób: byłyby o zakresie sposobu ich zawierającego, tj. niewidoczne i niedostępne poza ich treścią. I prawdopodobnie będą mieli dostęp do leksykalnego zakresu swojej metody zawierania. Jeśli jednak przeczytasz powyższą dyskusję, możesz zauważyć, że matz jest silnie przeciwny metodom zagnieżdżonym (ale wciąż do usuwania zagnieżdżonych definicji metod).

+6

Możesz również pokazać, jak utworzyć lambda zamknięcia w metodzie DRYness lub uruchomić rekursję. – Phrogz

+100

Mam wrażenie, że Ruby może nie mieć zagnieżdżonych metod. –

+11

@ Mark Thomas: Czy zapomniałem wspomnieć, że Ruby nie ma metod zagnieżdżonych? :-) Poważnie: w czasie gdy pisałem tę odpowiedź, były już trzy odpowiedzi, z których każda twierdziła, że ​​Ruby * ma * zagnieżdżone metody. Niektóre z tych odpowiedzi miały nawet poparcie, mimo że były rażąco błędne. Jeden został nawet zaakceptowany przez PO, mimo że był błędny. Fragment kodu, którego odpowiedź używa do udowodnienia, że ​​Ruby obsługuje metody zagnieżdżone, faktycznie udowadnia, że ​​jest odwrotnie, ale najwyraźniej ani przechodnie, ani OP nie zadali sobie trudu, aby to sprawdzić. Tak, dałem jedną właściwą odpowiedź dla każdego złego. :-) –

4

Nie, nie, Ruby ma zagnieżdżone metody.Sprawdź to:

def outer_method(arg) 
    outer_variable = "y" 
    inner_method = lambda { 
     puts arg 
     puts outer_variable 
    } 
    inner_method[] 
end 

outer_method "x" # prints "x", "y" 
+4

inner_method nie jest metodą, jest funkcją/lambda/proc. Nie ma powiązanej instancji żadnej klasy, więc nie jest to metoda. –

-1

:-D

Ruby zagnieżdżone metod, tylko nie robią to, czego można oczekiwać od nich

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" 
+3

To nie jest zagnieżdżona metoda ... zobacz odpowiedź * Jörga W Mittaga * dla jasnego zrozumienia. – Hardik

1

można zrobić coś takiego

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 

Jest to przydatne, gdy robisz rzeczy takie jak pisanie DSL, które wymagają podziału zakresu między metodami. Ale w przeciwnym razie znacznie lepiej radzisz sobie z robieniem czegokolwiek innego, ponieważ zgodnie z innymi odpowiedziami, inner jest ponownie definiowane, gdy wywoływane jest outer. Jeśli chcesz tego zachowania, a czasami możesz, jest to dobry sposób, aby go uzyskać.

9

W rzeczywistości jest to możliwe. Możesz użyć do tego proc/lambda.

def test(value) 
    inner = ->() { 
    value * value 
    } 
    inner.call() 
end 
0

Ruby sposobem jest fałszywy to z mylących hacki, które będą miały niektórzy użytkownicy zastanawiasz się: „Jak się do cholery robi to jeszcze działa?”, Podczas gdy mniej ciekawy będzie po prostu zapamiętać składnię potrzebne do korzystania z rzeczy. Jeśli kiedykolwiek używałeś Rake'a lub Railsa, widziałeś tego rodzaju rzeczy.

Oto taki 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 

Co to robi jest zdefiniować metodę najwyższego poziomu, który tworzy klasę i przekazuje go do bloku. Klasa używa method_missing, aby udawać, że ma metodę o wybranej nazwie. "Wdraża" tę metodę, wywołując lambdę, którą musisz podać. Nadając nazwę jednoliterowej nazwie, można zminimalizować ilość dodatkowego wpisywania, które jest wymagane (co jest tym samym co Rails w jego schema.rb). mlet nosi nazwę od Common Lisp form flet, z wyjątkiem, gdzie f oznacza "funkcja", m oznacza "metodę".

go używać tak:

def outer 
    mlet :inner, ->(x) { x*2 } do |c| 
    c.inner 12 
    end 
end 

Jest możliwe, aby podobny przyrząd, który pozwala na wiele funkcji wewnętrznych, które zostaną zdefiniowane bez dodatkowego gniazdowania, ale to wymaga jeszcze brzydsze siekać w tym rodzaju, który może Cię znaleźć w implementacji Rake'a lub Rspec. Ustalenie, w jaki sposób działa Rspec let!, dałoby ci długą drogę do stworzenia tak straszliwej obrzydliwości.

Powiązane problemy