我在方法内部有一个方法。内部方法取决于正在运行的变量循环。那是个坏主意吗?
Answers:
更新:由于这个答案最近似乎引起了人们的兴趣,我想指出,关于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在很大程度上反对嵌套方法(但仍然适用于删除嵌套方法定义)。
不,不,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的方法是使用令人迷惑的hack来伪造它,这会让一些用户怀疑“这到底是怎么工作的?”,而不太好奇的人只会记住使用该东西所需的语法。如果您曾经使用过Rake或Rails,那么您已经看到了这种事情。
这是一个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
这样做是定义一个顶级方法,该方法创建一个类并将其传递给块。该类用来method_missing
假装它具有使用您选择的名称的方法。它通过调用必须提供的lambda来“实现”该方法。通过用一个字母的名称命名该对象,可以最大程度地减少其所需的额外输入量(这与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的实现中可能会遇到的更加丑陋的hack。弄清楚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"