Project1

标题: Ruby 中往往会被忽略的地方 [ incomplete ] [打印本页]

作者: 忧雪の伤    时间: 2012-8-5 14:48
标题: Ruby 中往往会被忽略的地方 [ incomplete ]
本帖最后由 忧雪の伤 于 2012-8-6 15:17 编辑



前言:以下内容没有一个是你在平常的游戏制作中需要的,所以纯粹是来看教学的可以绕道了。


① 元类 ( metaclass )

先看一段代码。
  1. class << foo = Object.new
  2.   def foo
  3.     p 0
  4.   end
  5. end
  6. foo.foo
复制代码
class << class 有什么区别呢?为什么可以在一个不是类的对象前使用呢?
其实这个行为相当于把一个匿名类混合插入(mix-in)进对象,这个行为就和 include 相似。  
所以对象就会拥有这些新的实例方法了。
以上的行为就像添加对象的特殊方法,并不会影响到原本的类和其他实例。
(实际上不少人都把它这样用了,例如有人说可以用这个方式来代替 Module#module_function
这个行为有个好处就是可以用类的方法,比如 Class#attr_accessorClass#alias_method什么的。你甚至可以往这里 include,不污染到原本的类什么的。
  1. class << foo = Object.new
  2.   def foo
  3.     p 0
  4.   end
  5.   alias_method :bar, :foo
  6. end
  7. foo.bar
复制代码
不过说到底这些根本很少会用到吧?
这个匿名类也是一个实际存在的对象,我们可以通过编写一个快捷的方法来获得它。
  1. class Object
  2.   def metaclass
  3.     class << self
  4.       self
  5.     end
  6.   end
  7. end
  8. p Object.new.metaclass
复制代码
就可以获取到了~至于这个对象有什么用就是你要研究的事情了。
元类是个很神奇的东西,笔者有很多事情到现在都无法理解。(不会告诉你笔者是才刚懂的)
元类也有一种叫法叫单例类(Singleton Class)。(从异常报告来看似乎 Ruby 自己的叫法就是这个)

② 过程对象 ( Proc )

RPG Maker 中,如果你需要一个变量或者常量随时返回一个 #Game_Variable 的值怎么办?
难道是 var = $game_variable[var_id] #Fixnum 是即时值,var 并不会随着 $game_variable[var_id] 的改变而改变。
于是你可以这样做。
  1. block = proc { $game_variable[var_id] }
复制代码
当然这时直接 p block 返回的是一个 #Proc,并不是我们想要的 #Fixnum,我们需要让它启动,因为这实际上处于一个封装的状态。
  1. var = block.call
  2. p var
复制代码
这样就能让它启动。是不是很神奇呢?
实际上这和方法的原理相同,都是封装的过程,在你调用的时候进行计算。
而且一样可以带参数(块的参数),例如。
  1. block = proc {|arg| p arg }
  2. p block.call 0
复制代码
这简直就像无名函数一样。
过程对象在构建的时候会导入当前作用域的局部变量,这需要注意。
  1. foo = 0
  2. proc { p foo }
复制代码
RPG Maker XP 的默认脚本中就用到了这个来传递实例变量(虽然我觉得根本没这个必要……)

Object#instance_eval

如果想在当前作用域中进入其他对象的作用域怎么办?
Ruby 为我们提供了非常便捷的方法,那就是 instance_eval
它可以干什么呢?
例如,为对象定义一个它的实例变量?
  1. Object.new.instance_eval { @foo = 0 }
复制代码
实际上,这个方法是在调用对象的作用域中计算所带的块。
可以做到一些很方便的事情哦,毕竟是跨了作用域。当然要怎么玩都由你们自己发现了。
此外它也支持像 eval 一样计算字符串的功能,例如。
  1. Object.new.instance_eval 'p self'
复制代码

④方法对象 ( Method )

如果需要在一个对象调用另外一个对象的方法怎么办(这两个对象本身作用域不同)?
Ruby 有一个特殊的类 Method,它不能通过 Class#new 生成实例,而是要通过 Object#method 来生成它的实例。
Object#method 可以把对象的方法变为方法对象,把方法和被调对象都封装在了一起,例如。
  1. class Foo
  2.   def foo
  3.   end
  4. end
  5. foo = Foo.new
  6. method = foo.method :foo
  7. p method
复制代码
如果执行这个对象,相当于调用了那个方法。
  1. class Foo
  2.   @@foo = []
  3.   def foo
  4.     @@foo << 0
  5.   end
  6. end
  7. method = Foo.new.method :foo
  8. method.call
  9. class Foo
  10.   p @@foo
  11. end
复制代码
所以才能做到跨作用域调用原对象的方法。
因为是封装,所以即使之后原来的方法被再定义也还是不会有变动。
  1. class Foo
  2.   @@foo = []
  3.   def foo
  4.     @@foo << 0
  5.   end
  6. end
  7. method = Foo.new.method :foo
  8. class Foo
  9.   def foo
  10.     @@foo.pop
  11.   end
  12. end
  13. method.call
  14. class Foo
  15.   p @@foo
  16. end
  17. Foo.new.foo
  18. class Foo
  19.   p @@foo
  20. end
复制代码
Ruby 还提供了一个非常有用的方法,那就是 Method#receiver,可以返回原来的被调对象。
  1. class Foo
  2.   def foo;  end
  3. end
  4. foo = Foo.new
  5. foo.equal? foo.method(:foo).receiver
复制代码


结言:大家看完了之后是不是感觉被坑了呢?反正笔者自己感觉是的。笔者暂时想到的就这么多,如果有其他新奇的东西也请麻烦提出来吧~ → 其实笔者自己也是个小白。



作者: feizhaodan    时间: 2012-8-5 15:41
本帖最后由 feizhaodan 于 2012-8-5 15:41 编辑

我会说我之前玩instance_eval class_eval啥的把自己玩傻了么= =

  1. class << foo = Object.new
复制代码
这个的语法求解= =
作者: 忧雪の伤    时间: 2012-8-5 15:44
feizhaodan 发表于 2012-8-5 15:41
我会说我之前玩instance_eval class_eval啥的把自己玩傻了么= =

这个的语法求解= = ...

UI> 就是 foo = Object.new;  class << foo 而已啦……
      话说还真有人吐槽这个……囧,写的时候就猜到了。
作者: 幻想乡茶农    时间: 2012-8-5 16:10
Continuation和ObjectSpace都是冷门的东西,但有时候很有意思
作者: 苏小脉    时间: 2012-8-6 03:55
这个匿名类也是一个实际存在的对象,我们可以通过编写一个快捷的方法来获得它。

1.9 以後可以用 Object#singleton_class。

元类也有一种叫法叫特殊类(Singleton Class)。

「Singleton Class」的翻譯是「單例類」,「特殊類」對應的是「Eigenclass」。
作者: 忧雪の伤    时间: 2012-8-6 10:49
本帖最后由 忧雪の伤 于 2012-8-6 11:04 编辑
苏小脉 发表于 2012-8-6 03:55
1.9 以後可以用 Object#singleton_class。

特殊类
Singleton Class
只对应于某特定对象的假想类。

都怪我参考的是脑残的中文手册么……

前面那个方法还真的第一次见呢。
作者: 九夜神尊    时间: 2012-8-6 12:36
看不球懂,知道是好东西,果断收藏,几年后再来看!




欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1