设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 2424|回复: 4
打印 上一主题 下一主题

[RMVA发布] self 简介

[复制链接]

Lv3.寻梦者 (版主)

…あたしは天使なんかじゃないわ

梦石
0
星屑
2208
在线时间
4033 小时
注册时间
2010-10-4
帖子
10779

开拓者贵宾

跳转到指定楼层
1
发表于 2014-8-11 12:51:31 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有帐号?注册会员

x
本帖最后由 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 }


但是这样就破坏了类的封装性,所以在不必要的时候还是少用为好。   

评分

参与人数 5星屑 +150 收起 理由
余烬之中 + 30 破坏队形 我看了
kuerlulu + 30 怒赞!(其实我还没看
晴兰 + 30 怒赞!(其实我还没看
satgo1546 + 30 怒赞!(其实我还没看
VIPArcher + 30 怒赞!(其实我还没看

查看全部评分

Lv4.逐梦者 (版主)

百合控

梦石
0
星屑
6543
在线时间
1275 小时
注册时间
2013-8-21
帖子
3657

开拓者

2
发表于 2014-8-11 22:22:13 | 只看该作者
本帖最后由 余烬之中 于 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

点评

是啊 虽然知道是这样 但我一直没有具体的意识到  发表于 2014-8-12 09:44
class...end def...end 这些会分割局部变量作用域  发表于 2014-8-12 09:40
其实倒不是很难 只是第一次试验发现了一个本就该这样但潜意识里一直以为不是这样的事实(局部变量会因定义结束而死亡不会保存到下次定义)  发表于 2014-8-12 09:38
兰触曾经黑过 Proc#self= 方法。另外这是一个无关物:http://rm.66rpg.com/thread-367691-1-1.html  发表于 2014-8-12 07:19
binding我是知道的……自从上次学习了eval的四个参数…………看已经编辑的第三段 有更简洁的方法吗  发表于 2014-8-11 22:40
萌新瑟瑟发抖
看到我请叫我去干活
回复 支持 反对

使用道具 举报

Lv3.寻梦者 (版主)

…あたしは天使なんかじゃないわ

梦石
0
星屑
2208
在线时间
4033 小时
注册时间
2010-10-4
帖子
10779

开拓者贵宾

3
 楼主| 发表于 2014-8-12 08:00:54 | 只看该作者
余烬之中 发表于 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

点评

这个我能理解 两个proc访问的lvar在内存中是同一个位置 而关闭模块再打开定义lvar就是一个新位置的lvar 原先在模块中lvar的句柄已经死亡了  发表于 2014-8-12 09:36
回复 支持 反对

使用道具 举报

Lv3.寻梦者 (版主)

…あたしは天使なんかじゃないわ

梦石
0
星屑
2208
在线时间
4033 小时
注册时间
2010-10-4
帖子
10779

开拓者贵宾

4
 楼主| 发表于 2014-8-14 19:13:05 | 只看该作者
本帖最后由 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]


↑ 这是示例代码,拿保留字神马的做关键字参数= =
回复 支持 反对

使用道具 举报

Lv4.逐梦者 (版主)

百合控

梦石
0
星屑
6543
在线时间
1275 小时
注册时间
2013-8-21
帖子
3657

开拓者

5
发表于 2014-8-14 19:36:00 | 只看该作者
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}

点评

等等 binding也实现了local_variables吗?那个全列表 我以为只有Kernel实现了  发表于 2014-8-14 19:42
我爸意外的提前回家 就把我妈拉去逛街啦  发表于 2014-8-14 19:40
嗯,2.1已实现(话说你不是下线了吗)  发表于 2014-8-14 19:39
萌新瑟瑟发抖
看到我请叫我去干活
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-9-22 21:20

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表