【问题标题】:Optional parens in Ruby for method with uppercase start letter?Ruby中带有大写开头字母的方法的可选括号?
【发布时间】:2010-06-01 15:16:08
【问题描述】:

我刚开始在我的 .NET 应用程序中为 DSL 使用 IronRuby(但当我在纯 Ruby 中测试它时行为似乎一致) - 作为其中的一部分,我正在定义要通过 define_method 从 DSL 调用的方法.

但是,在调用以大写字母开头的方法时,我遇到了一个关于可选括号的问题。

给定以下程序:

class DemoClass
    define_method :test do puts "output from test" end
    define_method :Test do puts "output from Test" end

    def run
        puts "Calling 'test'"
        test()
        puts "Calling 'test'"
        test
        puts "Calling 'Test()'"
        Test()
        puts "Calling 'Test'"
        Test
    end
end

demo = DemoClass.new
demo.run

在控制台中运行此代码(使用纯 ruby​​)会产生以下输出:

ruby .\test.rb
Calling 'test'
output from test
Calling 'test'
output from test
Calling 'Test()'
output from Test
Calling 'Test'
./test.rb:13:in `run': uninitialized constant DemoClass::Test (NameError)
    from ./test.rb:19:in `<main>'

我意识到 Ruby 的约定是常量以大写字母开头,而 Ruby 中方法的一般命名约定是小写的。但是括号现在真的在扼杀我的 DSL 语法。

有没有办法解决这个问题?

【问题讨论】:

    标签: ruby ironruby


    【解决方案1】:

    这只是 Ruby 解决歧义的一部分。

    在 Ruby 中,方法和变量存在于不同的命名空间中,因此可以存在同名的方法和变量(或常量)。这意味着,当使用它们时,需要有某种方法来区分它们。一般来说,这不是问题:消息有接收者,变量没有。消息有参数,变量没有。变量被赋值,消息没有。

    唯一的问题是当你没有接收者、没有参数和没有赋值时。然后,Ruby 无法区分不带参数的无接收消息发送和变量之间的区别。所以,它必须组成一些任意规则,而这些规则基本上是:

    • 对于以小写字母开头的模棱两可的标记,更愿意将其解释为消息发送,除非您肯定知道它是一个变量(即 解析器(不是(!) 口译员)之前看过作业)
    • 对于以大写字母开头的模棱两可的标记,最好将其解释为常量

    请注意,对于带有参数的消息发送(即使参数列表为空),没有歧义,这就是您的第三个示例有效的原因。

    • test(): 明显是消息发送,这里没有歧义
    • test:可能是消息发送或变量;解析规则说是消息发送
    • Test():明明是消息发送,这里没有歧义
    • self.Test也是明显是消息发送,这里没有歧义
    • Test:可能是消息发送或常量;解析规则说它是一个常数

    请注意,这些规则有点微妙,例如这里:

    if false
      foo = 'This will never get executed'
    end
    
    foo # still this will get interpreted as a variable
    

    规则说,模棱两可的标记是被解释为变量还是消息发送由解析器决定,而不是解释器。所以,因为解析器已经看到了foo = whatever,它会将foo标记为一个变量,即使代码永远不会被执行并且foo将评估为nil,就像Ruby中所有未初始化的变量一样。

    TL;DR 总结:你是 SOL。

    可以做的是覆盖const_missing 以转换为消息发送。像这样的:

    class DemoClass
      def test; puts "output from test" end
      def Test; puts "output from Test" end
    
      def run
        puts "Calling 'test'"
        test()
        puts "Calling 'test'"
        test
        puts "Calling 'Test()'"
        Test()
        puts "Calling 'Test'"
        Test
      end
    
      def self.const_missing(const)
        send const.downcase
      end
    end
    
    demo = DemoClass.new
    demo.run
    

    除非这显然行不通,因为const_missing 是在DemoClass 上定义的,因此,当const_missing 运行时,selfDemoClass,这意味着它会在它尝试调用DemoClass.test应该通过demo.test 调用DemoClass#test

    我不知道如何轻松解决这个问题。

    【讨论】:

    • 顺便说一句:使用test 作为方法名是一个非常糟糕的主意。核心库中已经定义了一个名为 Kernel#test 的方法,如果尝试调试元编程编程,这可能真的搞砸你的思维过程。当我在上面的代码示例中期待 NoMethodError 时遇到 ArgumentError 异常时,这确实让我感到困惑!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-22
    • 2022-01-09
    • 2019-07-02
    • 1970-01-01
    • 2014-01-02
    • 2022-01-06
    • 2016-07-17
    相关资源
    最近更新 更多