Ale to nie jest metoda zagnieżdżona. Powtarzam: Ruby nie ma zagnieżdżonych metod.
To jest dynamiczna definicja metody. Kiedy uciekniesz meth1, ciało meth1zostanie wykonane. Po prostu ciało definiuje metodę o nazwie meth2, dlatego po meth1jednokrotnym uruchomieniu możesz wywołać meth2.
Ale gdzie jest meth2zdefiniowane? Cóż, oczywiście nie jest zdefiniowana jako metoda zagnieżdżona, ponieważ w Rubim nie ma zagnieżdżonych metod. Jest zdefiniowany jako metoda instancji Test1:
Test1.new.meth2
# Yay
Poza tym będzie on oczywiście przedefiniowywany za każdym razem, gdy uruchomisz 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 obsługuje zagnieżdżonych metod.
Zauważ również, że w Rubim, ciała metod nie mogą być zamknięciami, mogą nimi być tylko obiekty blokowe. To praktycznie eliminuje główny przypadek użycia metod zagnieżdżonych, ponieważ nawet jeśli Ruby obsługuje metody zagnieżdżone, nie można użyć zmiennych metody zewnętrznej w metodzie zagnieżdżonej.
AKTUALIZACJA KONTYNUOWANA: na późniejszym etapie ta składnia może zostać ponownie użyta do dodania zagnieżdżonych metod do Rubiego, co zachowywałoby się tak, jak opisałem: byłyby ograniczone do ich metody zawierającej, tj. Byłyby niewidoczne i niedostępne poza ich metodą zawierającą ciało. Być może mieliby dostęp do zakresu leksykalnego swojej metody zawierającej. Jeśli jednak przeczytasz dyskusję, do której dołączyłem powyżej, możesz zauważyć, że matz jest silnie przeciwny metodom zagnieżdżonym (ale nadal usuwa definicje metod zagnieżdżonych).
Możesz również pokazać, jak utworzyć lambdę zamknięcia w metodzie DRYness lub uruchomić rekursję.
Phrogz
119
Mam wrażenie, że Ruby może nie mieć zagnieżdżonych metod.
Mark Thomas
16
@Mark Thomas: Czy zapomniałem wspomnieć, że Ruby nie ma zagnieżdżonych metod? :-) Poważnie: w tym czasie pisałem tę odpowiedź, nie były już trzy odpowiedzi, z których każdy jeden twierdził, że Ruby nie posiada metody zagnieżdżonych. Niektóre z tych odpowiedzi otrzymały nawet głosy za, mimo że były rażąco błędne. Jeden z nich został nawet zaakceptowany przez PO, mimo że się mylił. Fragment kodu, którego używa odpowiedź, aby udowodnić, że Ruby obsługuje zagnieżdżone metody, w rzeczywistości udowadnia coś przeciwnego, ale najwyraźniej ani użytkownicy upvoters, ani OP nie zawracali sobie głowy sprawdzaniem. Więc podałem jedną dobrą odpowiedź na każdą złą. :-)
Jörg W Mittag
10
Kiedy zdasz sobie sprawę, że są to tylko instrukcje dla jądra, które modyfikują tabele i że metody, klasy i moduły są tylko wpisami w tabelach i nie są tak naprawdę rzeczywiste, stajesz się jak Neo, gdy widzi, jak wygląda Matrix. Wtedy można by naprawdę stać się filozofem i powiedzieć, że poza metodami zagnieżdżonymi nie ma nawet metod. Nie ma nawet agentów. Są to programy w macierzy. Nawet ten soczysty stek, który jesz, to tylko wpis na stole.
mydoghasworms
3
Nie ma żadnych metod, twój kod jest tylko symulacją w Matrixie
bbozo
13
Właściwie to możliwe. Możesz do tego użyć procs / lambda.
deftest(value)
inner = ->() {
value * value
}
inner.call()
end
Nie mylisz się, ale twoja odpowiedź jest sformułowana jako rozwiązanie umożliwiające osiągnięcie zagnieżdżonych metod. Kiedy w rzeczywistości używasz tylko procsów, które nie są metodami. To dobra odpowiedź poza twierdzeniem, że rozwiązuje "zagnieżdżone metody"
Brandon Buck
5
Nie, nie, Ruby ma zagnieżdżone metody. Sprawdź to:
defouter_method(arg)
outer_variable = "y"
inner_method = lambda {
puts arg
puts outer_variable
}
inner_method[]
end
outer_method "x"# prints "x", "y"
Jest to przydatne, gdy robisz takie rzeczy, jak pisanie DSL, które wymagają dzielenia zakresu między metodami. Ale w przeciwnym razie znacznie lepiej zrobisz cokolwiek innego, ponieważ jak mówią inne odpowiedzi, innerjest przedefiniowywana za każdym razem, gdy outerjest wywoływana. Jeśli chcesz takiego zachowania, a czasami możesz, jest to dobry sposób, aby to osiągnąć.
Sposób w Ruby polega na sfałszowaniu go za pomocą mylących hacków, które sprawią, że niektórzy użytkownicy będą się zastanawiać „Jak to do cholery w ogóle działa?”, Podczas gdy mniej ciekawi po prostu zapamiętają składnię potrzebną do użycia tej rzeczy. Jeśli kiedykolwiek korzystałeś z Rake lub Rails, widziałeś tego rodzaju rzeczy.
Oto taki 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
To, co robi, to zdefiniowanie metody najwyższego poziomu, która tworzy klasę i przekazuje ją do bloku. Klasa używa method_missingdo udawania, że ma metodę o wybranej przez Ciebie nazwie. „Implementuje” metodę, wywołując lambdę, którą musisz podać. Nazywając obiekt jednoliterową nazwą, możesz zminimalizować ilość dodatkowego wpisywania, którego wymaga (co jest tym samym, co robi w nim Rails schema.rb). mletjest nazwany na podstawie formy Common Lisp flet, z wyjątkiem sytuacji, gdy foznacza „funkcję”, moznacza „metodę”.
Możliwe jest stworzenie podobnego urządzenia, które pozwala na zdefiniowanie wielu funkcji wewnętrznych bez dodatkowego zagnieżdżania, ale wymaga to jeszcze brzydszego hackowania, takiego jaki można znaleźć w implementacji Rake'a lub Rspec. Dowiedzenie się, jak let!działa Rspec, pozwoliłoby ci stworzyć tak okropną obrzydliwość.
Odpowiedzi:
AKTUALIZACJA: Ponieważ ta odpowiedź wydaje się ostatnio wzbudzać zainteresowanie, chciałem zwrócić uwagę, że toczy się dyskusja na temat modułu śledzenia problemów w Rubim, aby usunąć omawianą tutaj funkcję, a mianowicie zakazać umieszczania definicji metod w treści metody .
Nie, Ruby nie ma zagnieżdżonych metod.
Możesz zrobić coś takiego:
class Test1 def meth1 def meth2 puts "Yay" end meth2 end end Test1.new.meth1
Ale to nie jest metoda zagnieżdżona. Powtarzam: Ruby nie ma zagnieżdżonych metod.
To jest dynamiczna definicja metody. Kiedy uciekniesz
meth1
, ciałometh1
zostanie wykonane. Po prostu ciało definiuje metodę o nazwiemeth2
, dlatego pometh1
jednokrotnym uruchomieniu możesz wywołaćmeth2
.Ale gdzie jest
meth2
zdefiniowane? Cóż, oczywiście nie jest zdefiniowana jako metoda zagnieżdżona, ponieważ w Rubim nie ma zagnieżdżonych metod. Jest zdefiniowany jako metoda instancjiTest1
:Test1.new.meth2 # Yay
Poza tym będzie on oczywiście przedefiniowywany za każdym razem, gdy uruchomisz
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 obsługuje zagnieżdżonych metod.
Zauważ również, że w Rubim, ciała metod nie mogą być zamknięciami, mogą nimi być tylko obiekty blokowe. To praktycznie eliminuje główny przypadek użycia metod zagnieżdżonych, ponieważ nawet jeśli Ruby obsługuje metody zagnieżdżone, nie można użyć zmiennych metody zewnętrznej w metodzie zagnieżdżonej.
AKTUALIZACJA KONTYNUOWANA: na późniejszym etapie ta składnia może zostać ponownie użyta do dodania zagnieżdżonych metod do Rubiego, co zachowywałoby się tak, jak opisałem: byłyby ograniczone do ich metody zawierającej, tj. Byłyby niewidoczne i niedostępne poza ich metodą zawierającą ciało. Być może mieliby dostęp do zakresu leksykalnego swojej metody zawierającej. Jeśli jednak przeczytasz dyskusję, do której dołączyłem powyżej, możesz zauważyć, że matz jest silnie przeciwny metodom zagnieżdżonym (ale nadal usuwa definicje metod zagnieżdżonych).
źródło
Właściwie to możliwe. Możesz do tego użyć procs / lambda.
def test(value) inner = ->() { value * value } inner.call() end
źródło
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"
źródło
Możesz 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 takie rzeczy, jak pisanie DSL, które wymagają dzielenia zakresu między metodami. Ale w przeciwnym razie znacznie lepiej zrobisz cokolwiek innego, ponieważ jak mówią inne odpowiedzi,
inner
jest przedefiniowywana za każdym razem, gdyouter
jest wywoływana. Jeśli chcesz takiego zachowania, a czasami możesz, jest to dobry sposób, aby to osiągnąć.źródło
Sposób w Ruby polega na sfałszowaniu go za pomocą mylących hacków, które sprawią, że niektórzy użytkownicy będą się zastanawiać „Jak to do cholery w ogóle działa?”, Podczas gdy mniej ciekawi po prostu zapamiętają składnię potrzebną do użycia tej rzeczy. Jeśli kiedykolwiek korzystałeś z Rake lub Rails, 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
To, co robi, to zdefiniowanie metody najwyższego poziomu, która tworzy klasę i przekazuje ją do bloku. Klasa używa
method_missing
do udawania, że ma metodę o wybranej przez Ciebie nazwie. „Implementuje” metodę, wywołując lambdę, którą musisz podać. Nazywając obiekt jednoliterową nazwą, możesz zminimalizować ilość dodatkowego wpisywania, którego wymaga (co jest tym samym, co robi w nim Railsschema.rb
).mlet
jest nazwany na podstawie formy Common Lispflet
, z wyjątkiem sytuacji, gdyf
oznacza „funkcję”,m
oznacza „metodę”.Używasz tego w ten sposób:
def outer mlet :inner, ->(x) { x*2 } do |c| c.inner 12 end end
Możliwe jest stworzenie podobnego urządzenia, które pozwala na zdefiniowanie wielu funkcji wewnętrznych bez dodatkowego zagnieżdżania, ale wymaga to jeszcze brzydszego hackowania, takiego jaki można znaleźć w implementacji Rake'a lub Rspec. Dowiedzenie się, jak
let!
działa Rspec, pozwoliłoby ci stworzyć tak okropną obrzydliwość.źródło
:-RE
Ruby ma zagnieżdżone metody, ale nie robią one tego, czego można by od nich oczekiwać
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"
źródło