如何判断方法是“动态定义的方法”?

时间:2013-09-08 18:22:57

标签: ruby metaprogramming

我很难理解“动态方法”的概念。我发现此blog post有用,声称

  

“动态方法”是计算名称或实体的方法   在运行时,然后定义。

但是我怎么能告诉方法是动态定义的呢?

    class Foo
      def self.bar
        puts 'bar is statically defined'
      end

      define_method(:baz) do
        puts 'baz is dynamically defined'
      end
    end

    Foo.singleton_methods.include? :bar  #=> true
    Foo.instance_methods.include? :baz  #=> true 
    #??? shouldn't this be false according to the claim of 'dynamic methods'?

3 个答案:

答案 0 :(得分:4)

“动态”和“静态”方法之间没有太大区别,但#source_location可能会给你一些提示。对于内置方法,#source_location返回nil:

method( :puts ).source_location #=> nil

现在,为了演示“静态”和“动态”方法之间的区别,让我们install the pyper gemgem install pyper。 Pyper gem提供了car / cdr方法的Lispy概念的扩展。 (car表示“head”,即列表中的第一个元素。cdr表示“tail”,即除了第一个元素之外的所有元素。)因为car pyper “静态”定义cdr个方法,其#source_location已知:

require 'pyper'
x = ["See", "you", "later", "alligator"]

x.car
#=> "See"
x.cdr
#=> ["you", "later", "alligator"]
x.method( :car ).source_location
#=> ["/usr/local/lib/ruby/gems/2.0.0/gems/pyper-2.0.0/lib/pyper.rb", 12]
x.method( :cdr ).source_location
#=> ["/usr/local/lib/ruby/gems/2.0.0/gems/pyper-2.0.0/lib/pyper.rb", 13]

#source_location方法的返回值直接为您提供方法源所来自的文件和行。但是, pyper 还允许“动态”方法,这些方法尚未在静态源文件中定义:

x.τaτ # equivalent of #car
#=> "See"
x.τdeadτ # a custom method that sellects all letters except the 1st from the 4th word
#=> "lligator"
x.method( :τaτ ).source_location
#=> ["(eval)", 1]
x.method( :τdeadτ ).source_location
#=> ["(eval)", 1]

返回值["(eval)", 1]表示该方法是由eval语句定义的,如果需要,您可以将其视为“动态”。

答案 1 :(得分:2)

正如@Babai指出的那样,OP已被编辑为“不要过于宽泛”。我不敢再编辑OP,以免引起投票。尽管如此,我承诺回答广泛制定的第一版OP,或者更确切地说,问题是否。 1,程序员什么时候应该使用动态方法:

当有太多可能无法手动定义的具有不同方法名称的方法时,应使用动态方法定义。

最终,这是一个语言问题。编程时,每个程序员都在创建一种语言,即某些域的域特定语言(DSL)。这种语言可能是好的也可能是坏的。良好的DSL通常类似于目标领域的典型方式。有时,您不需要太多的方法名称,并且可以使用参数处理可变性。实际上,标准方法是使用有限数量的Ruby方法名称,并将其余信息作为参数和块提供。

然而,有时候,目标域的特征语言确实需要大量甚至无限数量的方法名称。例如,Lispy方法carcdr允许无限多种组合:caarcadrcdadar cad....dadaaddar,...显然,这些方法不能全部手动定义。可以想象只定义一个方法cxr,其中cxr( 'a' )代表carcxr( 'd' )代表cdrcxr( 'dada' )代表cdadar } 等等。但是,可以说,car / cdr方法家族会失去它的美丽。所以,相反,我在 pyper 中定义了#method_missingEnumerable类似的类,启用了任意字母组合(τaaττadττdadaτ,...),而无需事先静态定义方法。

我可以提供的另一个示例是我的sy物理单元库(gem install sy)。可以说,每小时100英里的速度可以很好地写成:

require 'sy/imperial'
100.mile.h(-1)

但是,我问自己,它是美丽的吗?为了使DSL更像人们在计量领域表达自己的方式,我决定添加Unicode指数:

100.mile.h⁻¹

为了换取好看的外表,有一个问题:可能有无限多的指数:-∞, ..., -3, -2, -1, 0, 1, 2, 3, 4, ..., +∞。同样,有必要通过#method_missing动态定义这些方法。这样,任意,甚至荒谬的指数都是可能的:

nonsense_magnitude = 1.m²⁷.s⁻⁵³

答案 2 :(得分:-1)

  1. def bar只是define_method(:baz) do的语法糖

  2. 实际上,“动态方法”与“正常方法”定义之后没有区别。

  3. define_method通常与method_missing一起使用,下面的代码是“动态方法”的一个很好的例子

    class Foo
      def method_missing(meth, *args, &block)
        eigenclass = class << self; self; end
        eigenclass.class_eval do
          define_method(meth) do
            puts "method '#{meth}' is dynamically defined"
          end
        end
        send(meth, *args, &block)
      end
    end
    
    foo = Foo.new
    puts foo.methods.include? :bar  # => false
    foo.bar                         # => method 'bar' is dynamically defined
    puts foo.methods.include? :bar  # => true