Ruby Practice – Reflection

Reflection对我来说是个新概念,其含义很简单,我们可以使用支持Reflection的语言写一个能够查看自身信息的程序。第一次接触Reflection,感觉它对于学习语言非常有帮助,我们可以写一些程序来了解语言和运行时系统,不用再去构造“shape, circle, rectangle, draw”之类书卷气十足的例子。

Ruby的Reflection支持从对象查看类,查看类的基类,程序中有那些全局变量、局部变量等等。查看对象的类型很简单,如:

$ irb
irb(main):001:0> 5.class
=> Fixnum

第一次发现程序可以这样写时觉得很惊奇,其实并不难理解,Ruby的立即数的类型是一个类,而不像其它语言那样是一种内建类型,所以,5实际上是一个对象,因此我们可以调用该对象的类型支持的方法,class就是其中之一,它返回该对象的类型。可以看出,5的类型是Fixnum。让我们看看文档里是怎么介绍Fixnum

$ ri -T Fixnum
------------------------------------------------ Class: Fixnum < Integer
     A +Fixnum+ holds +Integer+ values that can be represented in a
     native machine word (minus 1 bit). If any operation on a +Fixnum+
     exceeds this range, the value is automatically converted to a
     +Bignum+.

看来还有一种类型叫Bignum

$ ri -T Bignum
------------------------------------------------ Class: Bignum < Integer
     Bignum objects hold integers outside the range of Fixnum. Bignum
     objects are created automatically when integer calculations would
     otherwise overflow a Fixnum. When a calculation involving Bignum
     objects returns a result that will fit in a Fixnum, the result is
     automatically converted.

似乎没有什么特别值得关注的内容了,让我们回到前面的例子。需要特别指出的是,class的返回值并不是字符串,而是一个类对象,比如我们可以调用superclass查看Fixnum的基类是什么。

irb(main):002:0> 5.class.superclass
=> Integer

如果我们留意前面的文档,会发现第一行有Fixnum < IntegerBignum < Integer这样的表述,看来<是用来描述子类与基类关系的符号。我们的好奇心也被激起了,到底这是怎样一幅类的层次图呢?

$ cat class.rb
#!/usr/bin/ruby

c = 5.class

while c
  s = c.superclass
  if s then
    print c, " < "
  else
    print c
  end
  c = s
end

puts
$ ruby class.rb
Fixnum < Integer < Numeric < Object

噢,我们找到了Object

$ ri -T Object
---------------------------------------------------------- Class: Object
     +Object+ is the parent class of all classes in Ruby. Its methods
     are therefore available to all objects unless explicitly
     overridden.

单根?!这让我想起了使用Delphi的日子……

也许把c作为print的参数让人感觉它是个字符串,其实不然,只不过Ruby在这里做了一次自动的类型转换。下面的程序统计出当前共有352个类,如果想知道都有哪些,可以用puts o代替1。

irb(main):003:0> ObjectSpace.each_object(Class) {|o| 1 }
=> 352

使用ri可以详细了解ObjectSpaceObjectSpace#each_objectClass的详细解释。简单的说,它提供了一种方式遍历所有的类,并对每个类调用花括号之内的程序。两条竖线之间的o代表当前正被遍历到的类,这里的o有点像函数的参数,我们也可以使用其它变量名。下面的程序显示了一个更加完全的类层次:

$ cat objectspace.rb
#!/usr/bin/ruby

c = 5.class
level = 1
while c
  print "#{level}: #{c}"
  ObjectSpace.each_object(Class) do |o|
    print " #{o}" if c.superclass == o.superclass and c != o
  end
  puts
  c = c.superclass
  level += 1
end
$ ruby objectspace.rb
1: Fixnum Bignum
2: Integer Float
3: Numeric Binding UnboundMethod Method Proc Process::Status Time Dir File::Stat
 IO Range MatchData Regexp Struct Hash Array ThreadGroup Continuation Thread
 Exception String FalseClass TrueClass Data Symbol NilClass Module
4: Object

这里的类并不多,把每个类的文档看一遍,不算是过分的要求。

前面的例子都是从5开始的,我们还可以从另一个方向——字符串——做同样的事情,还是从”hello, world”开始吧

irb(main):004:0> "hello, world".class
=> String

接下来的部分就由大家自己完成吧。

好,我们再回到5。

irb(main):005:0> 5.methods

大家可以尝试一下,看看我们都可以对5做什么?不过返回的结果有点乱,不太好看,让我们整理一下。比如看有哪些操作符:

irb(main):006:0> 5.methods.grep(/^W*$/)

如果觉得结果太多也可以选择一部分:

irb(main):007:0> 5.methods.grep(/^w*$/)[0..5]
=> ["upto", "div", "object_id", "times", "singleton_methods", "taint"]

还可以先排序再筛选

irb(main):008:0> 5.methods.sort.grep(/^w*$/)[10..15]
=> ["divmod", "downto", "dup", "extend", "floor", "freeze"]

sortgrep都是基于数组的操作,与Perl中的类似。

其它的如respond_to?kind_of?instance_of?,以及Kernel#global_variablesKernel#local_variables等Reflection功能就留给大家自己玩吧。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据