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? 的时候会发生什么。
代码很简单,只有一行。
actor.max_level? # 假设 actor 已经赋值
actor.max_level? # 假设 actor 已经赋值
首先,我们显式地对 actor 调用了 max_level?,此时 self 被设为了 actor
然后执行 max_level 方法的内容:
首先我们遇见了 @level 。由于当前的 self 是 actor,因此 @level 访问了 actor 的实例变量。
接着又有一个隐式调用的方法 max_level。此时不改变 self,仍然对 actor 调用 max_level 方法。
最后调用 @level 的 >= 方法即可返回答案。
----------------------------------------
改变 self 的方式有如下几种。
最重要的一种刚刚已经说过,是方法的调用。
此外,还有类定义、模块定义也可以改变 self。
module SceneManager
p self
end
#=> SceneManager
module SceneManager
p self
end
#=> SceneManager
在类定义和模块定义中,self 就被设置为当前的类或模块。
在这个例子里,这意味着,此时访问的实例变量,以及方法的隐式调用,self 都是设置为 SceneManager 的。
别忘了,模块也是对象,也可以拥有自己的实例变量。
module SceneManager
#--------------------------------------------------------------------------
# ● 模块的实例变量
#--------------------------------------------------------------------------
@scene = nil # 当前场景实例
@stack = [] # 场景切换的记录
@background_bitmap = nil # 背景用的场景截图
#--------------------------------------------------------------------------
# ● 运行
#--------------------------------------------------------------------------
# 模块定义中,self 为 SceneManager。因此这里等价于 def SceneManager.run
def self.run
# ↓ 运行这里的代码时,self 一定会被设为 SceneManager
DataManager.init
Audio.setup_midi if use_midi?
# 因此,访问 @scene 是访问模块的实例变量,也就是上面初始化过的变量。
# 同时,由于 self 在这里一定是 SceneManager,因此 first_scene_class 方法
# 的隐式调用,接收者正是 SceneManager。
@scene = first_scene_class.new
@scene.main while @scene
end
end
module SceneManager
#--------------------------------------------------------------------------
# ● 模块的实例变量
#--------------------------------------------------------------------------
@scene = nil # 当前场景实例
@stack = [] # 场景切换的记录
@background_bitmap = nil # 背景用的场景截图
#--------------------------------------------------------------------------
# ● 运行
#--------------------------------------------------------------------------
# 模块定义中,self 为 SceneManager。因此这里等价于 def SceneManager.run
def self.run
# ↓ 运行这里的代码时,self 一定会被设为 SceneManager
DataManager.init
Audio.setup_midi if use_midi?
# 因此,访问 @scene 是访问模块的实例变量,也就是上面初始化过的变量。
# 同时,由于 self 在这里一定是 SceneManager,因此 first_scene_class 方法
# 的隐式调用,接收者正是 SceneManager。
@scene = first_scene_class.new
@scene.main while @scene
end
end
-------------------------------------------------------------
Proc 对象可以保留区块创建时的环境,包括 self。
module M
lvar = 5
$a_mysterious_proc = Proc.new { print self, lvar, "\n" }
end
$a_mysterious_proc.call # 输出 M5
module M
lvar = 5
$a_mysterious_proc = Proc.new { print self, lvar, "\n" }
end
$a_mysterious_proc.call # 输出 M5
……这货有啥用?很有用。
class Game_Interpreter
def setup_choices(params)
params[0].each {|s| $game_message.choices.push(s) }
$game_message.choice_cancel_type = params[1]
$game_message.choice_proc = Proc.new {|n| @branch[@indent] = n }
end
end
class Game_Interpreter
def setup_choices(params)
params[0].each {|s| $game_message.choices.push(s) }
$game_message.choice_cancel_type = params[1]
$game_message.choice_proc = Proc.new {|n| @branch[@indent] = n }
end
end
关键在方法的最后一句,Proc.new {|n| @branch[@indent] = n }
Proc 传来传去时,可以保留 self ,因此 @branch 等实例变量都可以正确地访问了。
事实上 method 对象也有异曲同工之妙。只不过没有 Proc 那么优美的特性罢了。
要改变 self,我们可以使用 instance_eval 方法。
module M
lvar = 5
$a_mysterious_proc = Proc.new { print self, lvar, "\n" }
end
"喵呜喵".instance_eval(&$a_mysterious_proc) # 输出 喵呜喵5
module M
lvar = 5
$a_mysterious_proc = Proc.new { print self, lvar, "\n" }
end
"喵呜喵".instance_eval(&$a_mysterious_proc) # 输出 喵呜喵5
在与 Proc 无关的代码中,我们也可以用 instance_eval 来改变 self。
$game_party.instance_eval { @gold = max_gold }
$game_party.instance_eval { @gold = max_gold }
但是这样就破坏了类的封装性,所以在不必要的时候还是少用为好。
作者: 余烬之中 时间: 2014-8-11 22:22
本帖最后由 余烬之中 于 2014-8-11 22:39 编辑
- module M
- lvar = 5
- $a_mysterious_proc = Proc.new { print self, lvar, "\n" }
- lvar = 4
- end
- "m".instance_eval(&$a_mysterious_proc) #=> m4
复制代码- module M
- lvar = 5
- $a_mysterious_proc = Proc.new { print self, lvar, "\n" }
- end
- module M
- lvar = 4
- end
- "m".instance_eval(&$a_mysterious_proc) #=> m5
复制代码 似乎proc会一直保存着对变量的追踪 但是一旦死亡后即便有重名也不再管了 在局部变量的时候索引仅由proc保存 (好像表述的不是很清楚 不过无所谓了 我不知道我在说什么ORZ)
======================
像这样?
module M
lvar = 5
$a_mysterious_proc = Proc.new { print self, lvar, "\n" }
end
module M
lvar = 4
end
"m".instance_eval(&$a_mysterious_proc.tap{|s| s.binding.eval("lvar = 1")}) #=> m1
module M
lvar = 5
$a_mysterious_proc = Proc.new { print self, lvar, "\n" }
end
module M
lvar = 4
end
"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保存 ...
module M
lvar = 5
$change_lvar = Proc.new { lvar += 1 }
$a_mysterious_proc = Proc.new { print self, lvar, "\n" }
end
$a_mysterious_proc.call # M5
$change_lvar.call
$a_mysterious_proc.call # M6
module M
lvar = 5
$change_lvar = Proc.new { lvar += 1 }
$a_mysterious_proc = Proc.new { print self, lvar, "\n" }
end
$a_mysterious_proc.call # M5
$change_lvar.call
$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?
def primes(begin: 2, end: 1000)
[binding.local_variable_get(:begin), 2].max.upto(binding.local_variable_get(:end)).each_with_object([]) do |i, array|
array << i unless (2...i).any? {|j| (i % j).zero?}
end
end
primes(end: 10) #=> [2, 3, 5, 7]
def primes(begin: 2, end: 1000)
[binding.local_variable_get(:begin), 2].max.upto(binding.local_variable_get(:end)).each_with_object([]) do |i, array|
array << i unless (2...i).any? {|j| (i % j).zero?}
end
end
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 |