3

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

例如,如果您尝试通过

alias :magic :class

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

(不过,这个小实验确实有一个有趣的结果。通常,如果没有显式标识符,您不能调用该Object#class方法;仅输入repl 会产生语法错误,因为它与关键字混淆了。但是,通过给方法起别名,解释器不再感到困惑,因此您可以使用没有标识符的别名。非常漂亮。)selfclassclassObject#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

现在,作为一个 Ruby n00b(r00b?),这样做有什么继承或传统上的坏处吗?例如,在某些方面它可能真的很昂贵或危险吗?

4

2 回答 2

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 继承)。不完全好。

于 2013-06-21T16:30:05.673 回答
1

alias如果您没有充分理由使用它,当然会产生成本。语言之所以是一种语言,是因为有很多人在使用它。

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

于 2013-06-21T16:25:12.190 回答