当前位置 : 主页 > 编程语言 > ruby >

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

来源:互联网 收集:自由互联 发布时间:2021-06-23
我刚开始学习 Ruby,但我已经有了很多想法,可以利用它作为OOPL的独特性.标题中恰当地描述了我的第一个:是否可以在Ruby中创建(或模拟)关键字?我在repl中玩了一下,发现了一些带有别名
我刚开始学习 Ruby,但我已经有了很多想法,可以利用它作为OOPL的独特性.标题中恰当地描述了我的第一个:是否可以在Ruby中创建(或模拟)关键字?我在repl中玩了一下,发现了一些带有别名的有趣的东西.

例如,如果您尝试通过说明对关键字类别名

alias :magic :class

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

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

现在,由于我对Ruby的了解有限,我相信一种模拟关键字的方法,例如,类似于这样的事情:

# 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]

用户定义的类型方法有效地模仿了class关键字.或者,您可以使用字符串而不是符号调用type,并在将其传递给Class.new时向aName添加to_sym调用.或两者兼顾!

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?),有什么遗传或传统上不好这样做?例如,它可能在某种程度上是非常昂贵或危险的吗?

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

块范围与类范围不同.此外,这不会正确处理类变量(尽管你不应该没有很好的理由使用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).不太好.

网友评论