У мене є метод всередині методу. Внутрішній метод залежить від циклу змінних, який виконується. Це погана ідея?
Відповіді:
ОНОВЛЕННЯ: Оскільки ця відповідь останнім часом викликала певний інтерес, я хотів би зазначити, що в трекері видань Ruby ведеться дискусія щодо видалення обговорюваної тут функції, а саме щодо заборони мати визначення методів всередині тіла методу .
Ні, у Ruby немає вкладених методів.
Ви можете зробити щось подібне:
class Test1
def meth1
def meth2
puts "Yay"
end
meth2
end
end
Test1.new.meth1
Але це не вкладений метод. Повторюю: Рубі не має вкладених методів.
Що це таке - це визначення динамічного методу. Коли ви запустите 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 полягає в тому, щоб підробити його з заплутаними хаками, через які деякі користувачі задаються питанням: "Як, чорт візьми, це взагалі працює?", Тоді як менш цікаві просто запам'ятають синтаксис, необхідний для використання цієї речі. Якщо ви коли-небудь користувались граблями або рейками, ви бачили подібні речі.
Ось такий хак:
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
щоб зробити вигляд, що він має метод із вибраною вами назвою. Він "реалізує" метод, викликаючи лямбду, яку ви повинні надати. Називаючи об'єкт однобуквеним іменем, ви можете мінімізувати кількість додаткового набору тексту, яке йому потрібно (що те саме, що робить Rails schema.rb
). mlet
названо за формою Common Lisp flet
, за винятком випадків, коли це f
означає "функція",m
означає "метод".
Ви використовуєте його так:
def outer
mlet :inner, ->(x) { x*2 } do |c|
c.inner 12
end
end
Можна зробити подібний вигад, який дозволяє визначати безліч внутрішніх функцій без додаткового вкладання, але для цього потрібен ще потворніший злом, подібний до того, який ви можете знайти в реалізації Rake або Rspec. З’ясувавши, як роботи Rspec let!
допоможуть вам створити таку жахливу гидоту.
:-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"