是否可以在Ruby中创建(或模拟)关键字?

时间:2013-06-21 16:11:07

标签: ruby metaprogramming

我刚刚开始学习Ruby,但我已经掌握了如何利用其作为OOPL的独特性的重要思想。标题中恰当地描述了我的第一个:是否可以在Ruby中创建(或模拟)关键字?我在repl中玩了一下,发现了一些带有别名的有趣的东西。

例如,如果您尝试通过说

对关键字 class 进行别名
alias :magic :class

似乎有效,因为它会输出nil。但是,它只是别名Object#class方法;我的猜测是没有办法为关键字设置别名,因为关键字不是常量,很可能硬编码到解释器本身。

(这个小实验确实有一个有趣的结果。通常,如果没有明确的Object#class标识符,就无法调用self方法;只需在repl中键入class即可生成语法错误,因为它与关键字class混淆。但是,通过别名Object#class方法,解释器不再混淆,因此您可以使用没有标识符的别名。非常漂亮。)

目前,由于我对Ruby的了解有限,我相信模拟关键字的方法,例如class,就是这样做:

# in some file Magic.rb
module Magic
  def self.type
    # do something to evaluate the given block as a class definition
    yield if block_given?
  end
end

Magic.type Awesome do
  def initialize;end

  def who_am_i?
    puts "I'm Awesome!"
  end
end

x = Awesome.new           # desired output: #<Awesome:0x1234abc>
x.who_am_i?               # desired output: "I'm Awesome!"

但这比我希望的更糟糕。有什么想法吗?

编辑:经过一些修补和谷歌搜索,我发现了我认为是一个很好的解决方案,利用匿名类实例化,块和Object#const_set

def type aName, &block
  Object.const_set(aName, Class.new(Array, &block))
end

type :AwesomeArray do
    def intialize
      puts "Initialized."
    end
    def magic
      puts "DO ALL THE MAGICKS!"
    end
end

x = AwesomeArray.new          # --> #<Awesome:0x12335abc>
puts x.is_a? AwesomeArray     # --> true
puts x.is_a? Array            # --> true
puts x.is_a? Object           # --> true
x.magic                       # --> "DO ALL THE MAGICKS!"
x |= [1, 2, 3]                # --> [1, 2, 3]

用户定义的type方法有效地模仿了class关键字。或者,您可以使用字符串而不是符号来呼叫type,并在将to_sym传递给aName时向Class.new添加def type aSymbol, &block Object.const_set(aSymbol, Class.new(Array, &block)) end def type_str aString, &block type aString.to_sym, &block end 次呼叫。或两者都做!

{{1}}

现在,作为一个Ruby n00b(r00b?),有没有继承或传统这样做?例如,它可能在某种程度上是非常昂贵或危险吗?

2 个答案:

答案 0 :(得分:6)

TL; DR:如果你没有充分的理由这样做,请不要

顺便说一下,你不能模拟关键词。它们内置于解析器中。所有你能做的就是你想出的元编程黑客......但是:


这并不是说效率低得多:

       user     system      total        real
type method:   0.000000   0.000000   0.000000 (  0.000034)
class keyword:   0.000000   0.000000   0.000000 (  0.000030)

这只是糟糕的形式。为什么用无用的方法使全局命名空间变得混乱,这使得你的代码更难以被其他人阅读?代码读取的内容远远超过编写代码。不要让别人或你自己的事情变得更难。

这也有一些范围问题,这些问题将在稍后发布。例如:

2.0.0p0 :001 > def type aName, &block                                                                                                                                                                                                              
2.0.0p0 :002?>     Object.const_set(aName, Class.new(Array, &block))                                                                                                                                                                               
2.0.0p0 :003?>   end                                                                                                                                                                                                                               
 => nil                                                                                                                                                                                                                                            
2.0.0p0 :004 > a = 'hi'
 => "hi" 
2.0.0p0 :005 > type :Hi1 do
2.0.0p0 :006 >     puts a
2.0.0p0 :007?>   end
hi
 => Hi1 
2.0.0p0 :008 > class Hi2
2.0.0p0 :009?>   puts a
2.0.0p0 :010?> end 
NameError: undefined local variable or method `a' for Hi2:Class
        from (irb):9:in `<class:Hi2>'
        from (irb):8

块范围与class范围不同。此外,这不会正确处理类变量(尽管你不应该在没有很好理由的情况下使用Ruby的类变量......):

2.0.0p0 :012 > type :T1 do
2.0.0p0 :013 >     @@t1test = 'hi'
2.0.0p0 :014?>   end
(irb):13: warning: class variable access from toplevel
 => T1 
2.0.0p0 :015 > T1.class_variables
 => [:@@t1test]  
2.0.0p0 :018 > class T2
2.0.0p0 :019?>   @@t2test = 'bork'
2.0.0p0 :020?>   end
 => "bork" 
2.0.0p0 :021 > Object.class_variables
 => [:@@t1test] 
2.0.0p0 :022 > T2.class_variables
 => [:@@t2test, :@@t1test] 

正如您所看到的,您的块形式将类变量泄漏到每个类(来自Object的后代)。不太好。

答案 1 :(得分:1)

当然,alias如果您没有充分理由使用它,则需要付费。语言是一种语言,因为有很多人使用它。

如果违反惯例,它就会变成你自己的语言。