Project1

标题: self 简介 [打印本页]

作者: taroxd    时间: 2014-8-11 12:51
标题: self 简介
本帖最后由 taroxd 于 2014-8-11 14:21 编辑

如果你是刚刚接触 ruby 不久的新人,那么请无视这个帖子。
如果你是大触,那么也请无视这个帖子。

总之,这个帖子的内容没什么用,都是我瞎说的,有可能错误百出,看着玩玩就可以了。

@Scene

----------------------------------------------------

self 是什么?
要简单地说,也很简单。self 就是当前对象,也就是当前方法的接收者(receiver)。

但是 self 的存在意义远远不止这些。

----------------------------------------------------

首先,self 决定了实例变量的访问对象。
几乎每个对象都可以携带实例变量。当你访问一个实例变量的时候,Ruby 会寻找 self 内部的实例变量。

此外,self 在方法调用的时候会发生变化。隐式调用并不改变 self。
比如,print 函数其实就是定义在 Kernel 模块的私有方法。
当你调用 print 233 的时候,事实上是对当前的对象 self 调用了 print 方法。
此时 self 没有被改变。

然而,当你显式调用方法时,self 就会被设为方法的接收者。
比如 $game_party.members 的时候,在 members 的执行过程中,self 就被设为了 $game_party 所引用的那一个对象。

我们看一看调用 Game_Actors#max_level? 的时候会发生什么。
代码很简单,只有一行。
RUBY 代码复制
  1. actor.max_level? # 假设 actor 已经赋值


首先,我们显式地对 actor 调用了 max_level?,此时 self 被设为了 actor
然后执行 max_level 方法的内容:
RUBY 代码复制
  1. @level >= max_level


首先我们遇见了 @level 。由于当前的 self 是 actor,因此 @level 访问了 actor 的实例变量。
接着又有一个隐式调用的方法 max_level。此时不改变 self,仍然对 actor 调用 max_level 方法。
最后调用 @level 的 >= 方法即可返回答案。

----------------------------------------

改变 self 的方式有如下几种。
最重要的一种刚刚已经说过,是方法的调用。
此外,还有类定义、模块定义也可以改变 self。
RUBY 代码复制
  1. module SceneManager
  2.   p self
  3. end
  4. #=> SceneManager

在类定义和模块定义中,self 就被设置为当前的类或模块。
在这个例子里,这意味着,此时访问的实例变量,以及方法的隐式调用,self 都是设置为 SceneManager 的。
别忘了,模块也是对象,也可以拥有自己的实例变量。
RUBY 代码复制
  1. module SceneManager
  2.   #--------------------------------------------------------------------------
  3.   # ● 模块的实例变量
  4.   #--------------------------------------------------------------------------
  5.   @scene = nil                            # 当前场景实例
  6.   @stack = []                             # 场景切换的记录
  7.   @background_bitmap = nil                # 背景用的场景截图
  8.   #--------------------------------------------------------------------------
  9.   # ● 运行
  10.   #--------------------------------------------------------------------------
  11.   # 模块定义中,self 为 SceneManager。因此这里等价于 def SceneManager.run
  12.   def self.run
  13.     # ↓ 运行这里的代码时,self 一定会被设为 SceneManager
  14.     DataManager.init
  15.     Audio.setup_midi if use_midi?
  16.     # 因此,访问 @scene 是访问模块的实例变量,也就是上面初始化过的变量。
  17.     # 同时,由于 self 在这里一定是 SceneManager,因此 first_scene_class 方法
  18.     # 的隐式调用,接收者正是 SceneManager。
  19.     @scene = first_scene_class.new
  20.     @scene.main while @scene
  21.   end
  22. end


-------------------------------------------------------------

Proc 对象可以保留区块创建时的环境,包括 self。
RUBY 代码复制
  1. module M
  2.   lvar = 5
  3.   $a_mysterious_proc = Proc.new { print self, lvar, "\n" }
  4. end
  5.  
  6. $a_mysterious_proc.call # 输出 M5


……这货有啥用?很有用。

RUBY 代码复制
  1. class Game_Interpreter
  2.   def setup_choices(params)
  3.     params[0].each {|s| $game_message.choices.push(s) }
  4.     $game_message.choice_cancel_type = params[1]
  5.     $game_message.choice_proc = Proc.new {|n| @branch[@indent] = n }
  6.   end
  7. end


关键在方法的最后一句,Proc.new {|n| @branch[@indent] = n }
Proc 传来传去时,可以保留 self ,因此 @branch 等实例变量都可以正确地访问了。
事实上 method 对象也有异曲同工之妙。只不过没有 Proc 那么优美的特性罢了。

要改变 self,我们可以使用 instance_eval 方法。

RUBY 代码复制
  1. module M
  2.   lvar = 5
  3.   $a_mysterious_proc = Proc.new { print self, lvar, "\n" }
  4. end
  5.  
  6. "喵呜喵".instance_eval(&$a_mysterious_proc) # 输出 喵呜喵5


在与 Proc 无关的代码中,我们也可以用 instance_eval 来改变 self。

RUBY 代码复制
  1. $game_party.instance_eval { @gold = max_gold }


但是这样就破坏了类的封装性,所以在不必要的时候还是少用为好。   
作者: 余烬之中    时间: 2014-8-11 22:22
本帖最后由 余烬之中 于 2014-8-11 22:39 编辑
  1. module M
  2.   lvar = 5
  3.   $a_mysterious_proc = Proc.new { print self, lvar, "\n" }
  4.   lvar = 4
  5. end

  6. "m".instance_eval(&$a_mysterious_proc) #=> m4
复制代码
  1. module M
  2.   lvar = 5
  3.   $a_mysterious_proc = Proc.new { print self, lvar, "\n" }
  4. end
  5. module M
  6.   lvar = 4
  7. end

  8. "m".instance_eval(&$a_mysterious_proc) #=> m5
复制代码
似乎proc会一直保存着对变量的追踪 但是一旦死亡后即便有重名也不再管了 在局部变量的时候索引仅由proc保存 (好像表述的不是很清楚 不过无所谓了 我不知道我在说什么ORZ)

======================

像这样?
RUBY 代码复制
  1. module M
  2.   lvar = 5
  3.   $a_mysterious_proc = Proc.new { print self, lvar, "\n" }
  4. end
  5. module M
  6.   lvar = 4
  7. end
  8.  
  9. "m".instance_eval(&$a_mysterious_proc.tap{|s| s.binding.eval("lvar = 1")}) #=> m1

作者: taroxd    时间: 2014-8-12 08:00
余烬之中 发表于 2014-8-11 22:22
似乎proc会一直保存着对变量的追踪 但是一旦死亡后即便有重名也不再管了 在局部变量的时候索引仅由proc保存 ...

RUBY 代码复制
  1. module M
  2.   lvar = 5
  3.   $change_lvar = Proc.new { lvar += 1 }
  4.   $a_mysterious_proc = Proc.new { print self, lvar, "\n" }
  5. end
  6.  
  7. $a_mysterious_proc.call # M5
  8. $change_lvar.call
  9. $a_mysterious_proc.call # M6

作者: taroxd    时间: 2014-8-14 19:13
本帖最后由 taroxd 于 2014-8-14 19:14 编辑
余烬之中 发表于 2014-8-11 22:22
似乎proc会一直保存着对变量的追踪 但是一旦死亡后即便有重名也不再管了 在局部变量的时候索引仅由proc保存 ...


我发现,在 Ruby2.1 中定义了 Binding#local_variable_get/_set/_defined?

RUBY 代码复制
  1. def primes(begin: 2, end: 1000)
  2.   [binding.local_variable_get(:begin), 2].max.upto(binding.local_variable_get(:end)).each_with_object([]) do |i, array|
  3.     array << i unless (2...i).any? {|j| (i % j).zero?}
  4.   end
  5. end
  6.  
  7. primes(end: 10)   #=> [2, 3, 5, 7]


↑ 这是示例代码,拿保留字神马的做关键字参数= =
作者: 余烬之中    时间: 2014-8-14 19:36
taroxd 发表于 2014-8-14 19:13
我发现,在 Ruby2.1 中定义了 Binding#local_variable_get/_set/_defined?

def primes(begin: 2, end: 1 ...

假设实现了local_variables这个方法 就可以实现很久以前兰兰提的问题
如何实现:convert{a = 3; b = 5} #=> { :a => 3, :b => 5}





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