Mam metodę wewnątrz metody. Metoda wewnętrzna zależy od zmiennej pętli, która jest uruchamiana. Czy to zły pomysł?
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ł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).
Właściwie to możliwe. Możesz do tego użyć procs / lambda.
def test(value)
inner = ->() {
value * value
}
inner.call()
end
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"
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, 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:
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_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ę”.
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ść.
:-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"