是否可以在方法内部使用方法?

时间:2011-02-01 15:07:14

标签: ruby methods

我在方法中有一个方法。内部方法依赖于正在运行的变量循环。这是个坏主意吗?

6 个答案:

答案 0 :(得分:154)

更新:由于这个答案最近似乎引起了一些兴趣,我想指出有discussion on the Ruby issue tracker to remove the feature discussed here, namely to forbid having method definitions inside a method body


不,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中,方法体不能是闭包,只能是块体。这几乎消除了嵌套方法的主要用例,因为即使 if Ruby支持嵌套方法,也不能在嵌套方法中使用外部方法的变量。


UPDATE CONTINUED:在以后的阶段,然后,这个语法可能会重新用于向Ruby添加嵌套方法,这将按照我所描述的方式运行:它们的作用域将包含在其包含的方法中,即在其包含方法体外的不可见和不可访问。并且可能,他们可以访问其包含方法的词法范围。但是,如果您阅读我上面链接的讨论,您可以观察到matz严重违反嵌套方法(但仍然用于删除嵌套方法定义)。

答案 1 :(得分:10)

实际上它是可能的。你可以使用procs / lambda。

def test(value)
  inner = ->() {
    value * value
  }
  inner.call()
end

答案 2 :(得分:5)

不,不,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"

答案 3 :(得分:1)

你可以做这样的事情

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。如果你想要这种行为,有时你可能会这样做,这是获得它的好方法。

答案 4 :(得分:0)

Ruby的方法是用令人困惑的黑客来伪造它,让一些用户想知道"他妈的如何做到这一点?",而不那么好奇只会记住使用事情。如果您曾经使用过Rake或Rails,那么您已经看过这种事情。

这是一个黑客攻击:

wc -l

它的作用是定义一个创建类并将其传递给块的顶级方法。该类使用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 假装它具有您选择的名称的方法。它"实现"通过调用必须提供的lambda方法。通过使用单个字母的名称命名对象,可以最大限度地减少所需的额外输入量(这与Rails在method_missing中执行的操作相同)。 schema.rb以Common Lisp表单mlet命名,除了flet代表"函数",f代表"方法&#34 ;

你这样使用它:

m

可以制作一个类似的装置,允许定义多个内部函数而无需额外的嵌套,但这需要一个更糟糕的黑客,你可能会在Rake或Rspec的实现中找到它。弄清楚Rspec的def outer mlet :inner, ->(x) { x*2 } do |c| c.inner 12 end end 如何运作会让你在创造如此可怕的憎恶方面有很长的路要走。

答案 5 :(得分:-3)

: - 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"