Но это не вложенный метод. Повторяю: в Ruby нет вложенных методов.
Это определение динамического метода. Когда вы запустите meth1, meth1будет выполнено тело . Тело просто определяет названный метод meth2, поэтому после meth1однократного запуска вы можете вызвать meth2.
Но где meth2определяется? Что ж, очевидно, что он не определен как вложенный метод, поскольку в Ruby нет вложенных методов. Он определяется как метод экземпляра Test1:
Test1.new.meth2
# Yay
Кроме того, очевидно, что он будет переопределяться каждый раз при запуске 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
Вкратце: нет, Ruby не поддерживает вложенные методы.
Также обратите внимание, что в Ruby тела методов не могут быть замыканиями, могут быть только тела блоков. Это в значительной степени исключает основной вариант использования вложенных методов, поскольку даже если Ruby поддерживает вложенные методы, вы не можете использовать переменные внешнего метода во вложенном методе.
ОБНОВЛЕНИЕ ПРОДОЛЖЕНИЕ: на более позднем этапе этот синтаксис может быть повторно использован для добавления вложенных методов в Ruby, которые будут вести себя так, как я описал: они будут привязаны к их содержащему методу, то есть невидимы и недоступны вне их содержащего метода тело. И, возможно, у них будет доступ к лексической области их содержащего метода. Однако, если вы прочитаете обсуждение, которое я связал выше, вы можете заметить, что matz сильно против вложенных методов (но все же для удаления определений вложенных методов).
Однако вы также можете показать, как создать закрывающую лямбду в методе DRYness или запустить рекурсию.
Phrogz
119
У меня такое чувство, что у Ruby может не быть вложенных методов.
Марк Томас
16
@Mark Thomas: Я забыл упомянуть, что Ruby не имеет вложенных методов? :-) Серьезно: на тот момент, когда я написал этот ответ, было уже три ответа, каждый из которых утверждал, что Ruby действительно имеет вложенные методы. Некоторые из этих ответов даже получили положительные отзывы, несмотря на то, что были явно ошибочными. Один даже был принят OP, опять же, несмотря на то, что он ошибался. Фрагмент кода, который используется в ответе, чтобы доказать, что Ruby поддерживает вложенные методы, на самом деле доказывает обратное, но, очевидно, ни сторонники, ни OP не потрудились проверить. Итак, я дал один правильный ответ на каждый неправильный. :-)
Jörg W Mittag
10
Когда вы понимаете, что это всего лишь инструкции для ядра, которые изменяют таблицы, и что методы, классы и модули - это просто записи в таблицах, а не на самом деле, вы становитесь похожими на Нео, когда он видит, как выглядит Матрица. Тогда вы действительно могли бы стать философом и сказать, что кроме вложенных методов нет даже методов. Нет даже агентов. Это программы в матрице. Даже тот сочный стейк, который вы едите, - это просто запись в таблице.
mydoghasworms 03
3
Нет никаких методов, ваш код - просто симуляция в Матрице
bbozo
13
На самом деле это возможно. Для этого вы можете использовать procs / lambda.
deftest(value)
inner = ->() {
value * value
}
inner.call()
end
Вы не ошиблись, но ваш ответ сформулирован как решение для достижения вложенных методов. Когда на самом деле вы просто используете процедуры, которые не являются методами. Это прекрасный ответ, если не считать утверждения о «вложенных методах»,
Брэндон Бак,
5
Нет-нет, у Ruby есть вложенные методы. Проверь это:
defouter_method(arg)
outer_variable = "y"
inner_method = lambda {
puts arg
puts outer_variable
}
inner_method[]
end
outer_method "x"# prints "x", "y"
Это полезно, когда вы делаете такие вещи, как написание DSL, которые требуют разделения области видимости между методами. Но в противном случае вам гораздо лучше делать что-нибудь еще, потому что, как сказано в других ответах, innerон переопределяется всякий раз, когда outerвызывается. Если вы хотите такого поведения, а иногда и хотите, это хороший способ добиться этого.
Способ Ruby - подделать его с помощью запутанных хаков, которые заставят некоторых пользователей задуматься: «Какого черта это вообще работает?», В то время как менее любопытные просто запоминают синтаксис, необходимый для использования этой штуки. Если вы когда-либо использовали Rake или Rails, вы видели подобные вещи.
Вот такой взлом:
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
Это означает определение метода верхнего уровня, который создает класс и передает его блоку. Класс использует, method_missingчтобы представить, что у него есть метод с выбранным вами именем. Он «реализует» метод, вызывая лямбду, которую вы должны предоставить. Называя объект однобуквенным именем, вы можете минимизировать количество требуемых дополнительных операций ввода (это то же самое, что делает в Rails schema.rb). mletназван в честь формы Common Lisp flet, за исключением случаев, когда f«функция» mозначает «метод».
Можно создать аналогичное устройство, которое позволяет определять несколько внутренних функций без дополнительных вложений, но для этого требуется еще более уродливый хакер, который вы можете найти в реализации Rake или Rspec. Разобравшись, как let!работает Rspec, вы сможете далеко продвинуться к созданию такой ужасной мерзости.
Ответы:
ОБНОВЛЕНИЕ: поскольку в последнее время этот ответ, похоже, вызвал некоторый интерес, я хотел указать, что ведется обсуждение трекера проблем Ruby, чтобы удалить обсуждаемую здесь функцию, а именно запретить определение методов внутри тела метода .
Нет, в Ruby нет вложенных методов.
Вы можете сделать что-то вроде этого:
class Test1 def meth1 def meth2 puts "Yay" end meth2 end end Test1.new.meth1
Но это не вложенный метод. Повторяю: в Ruby нет вложенных методов.
Это определение динамического метода. Когда вы запустите
meth1
,meth1
будет выполнено тело . Тело просто определяет названный методmeth2
, поэтому послеmeth1
однократного запуска вы можете вызватьmeth2
.Но где
meth2
определяется? Что ж, очевидно, что он не определен как вложенный метод, поскольку в Ruby нет вложенных методов. Он определяется как метод экземпляраTest1
:Test1.new.meth2 # Yay
Кроме того, очевидно, что он будет переопределяться каждый раз при запуске
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
Вкратце: нет, Ruby не поддерживает вложенные методы.
Также обратите внимание, что в Ruby тела методов не могут быть замыканиями, могут быть только тела блоков. Это в значительной степени исключает основной вариант использования вложенных методов, поскольку даже если Ruby поддерживает вложенные методы, вы не можете использовать переменные внешнего метода во вложенном методе.
ОБНОВЛЕНИЕ ПРОДОЛЖЕНИЕ: на более позднем этапе этот синтаксис может быть повторно использован для добавления вложенных методов в Ruby, которые будут вести себя так, как я описал: они будут привязаны к их содержащему методу, то есть невидимы и недоступны вне их содержащего метода тело. И, возможно, у них будет доступ к лексической области их содержащего метода. Однако, если вы прочитаете обсуждение, которое я связал выше, вы можете заметить, что matz сильно против вложенных методов (но все же для удаления определений вложенных методов).
источник
На самом деле это возможно. Для этого вы можете использовать procs / lambda.
def test(value) inner = ->() { value * value } inner.call() end
источник
Нет-нет, у Ruby есть вложенные методы. Проверь это:
def outer_method(arg) outer_variable = "y" inner_method = lambda { puts arg puts outer_variable } inner_method[] end outer_method "x" # prints "x", "y"
источник
Вы можете сделать что-то вроде этого
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
Это полезно, когда вы делаете такие вещи, как написание DSL, которые требуют разделения области видимости между методами. Но в противном случае вам гораздо лучше делать что-нибудь еще, потому что, как сказано в других ответах,
inner
он переопределяется всякий раз, когдаouter
вызывается. Если вы хотите такого поведения, а иногда и хотите, это хороший способ добиться этого.источник
Способ Ruby - подделать его с помощью запутанных хаков, которые заставят некоторых пользователей задуматься: «Какого черта это вообще работает?», В то время как менее любопытные просто запоминают синтаксис, необходимый для использования этой штуки. Если вы когда-либо использовали Rake или Rails, вы видели подобные вещи.
Вот такой взлом:
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
Это означает определение метода верхнего уровня, который создает класс и передает его блоку. Класс использует,
method_missing
чтобы представить, что у него есть метод с выбранным вами именем. Он «реализует» метод, вызывая лямбду, которую вы должны предоставить. Называя объект однобуквенным именем, вы можете минимизировать количество требуемых дополнительных операций ввода (это то же самое, что делает в Railsschema.rb
).mlet
назван в честь формы Common Lispflet
, за исключением случаев, когдаf
«функция»m
означает «метод».Вы используете это так:
def outer mlet :inner, ->(x) { x*2 } do |c| c.inner 12 end end
Можно создать аналогичное устройство, которое позволяет определять несколько внутренних функций без дополнительных вложений, но для этого требуется еще более уродливый хакер, который вы можете найти в реализации Rake или Rspec. Разобравшись, как
let!
работает Rspec, вы сможете далеко продвинуться к созданию такой ужасной мерзости.источник
:-D
В Ruby есть вложенные методы, но они не делают того, что от них ожидается.
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"
источник