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

Project1

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

[讨论] 今天被ruby摆了一道……查了一天的bug

[复制链接]

Lv3.寻梦者

梦石
0
星屑
1044
在线时间
251 小时
注册时间
2016-9-2
帖子
126
跳转到指定楼层
1
发表于 2016-9-6 17:02:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

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

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

x
c#转型rgss3的新人脚本君
今天正在愉快的阅读代码之中,偶然看到这样一句代码
  1.   def create_contents
  2.     contents.dispose
  3.     if contents_width > 0 && contents_height > 0
  4.       self.contents = Bitmap.new(contents_width, contents_height)
  5.     else
  6.       self.contents = Bitmap.new(1, 1)
  7.     end
  8.   end
复制代码

哦简单,这里的contents就是引用父类的属性吧,省略了self而已
所以我加了self,果然如我所料,没有任何问题
所以这里的self.contents到底为什么不简化一下呢,所以我改成了contents = Bitmap.new(contents_width, contents_height)
开始报错了……
于是我搜索,self和this的区别(我怀疑self比this有不同的用法,因为在这里self如果和this用法一样,且可以省略的话,这样的修改应该没有问题的)



查了无数代码,p了多少次地址,我都确定 p self.contents 和p contents是一个东西了……
最后,我灵感来了!
  1.       contents = Bitmap.new(contents_width, contents_height)
  2.       p contents
  3.       p self.contents
复制代码

新思路的检测代码
果然……
我去,这里的contents是被声明为了临时变量,所以才会报bug

一下午的光阴就在这坑爹的bug中度过了……

Lv5.捕梦者 (暗夜天使)

只有笨蛋才会看到

梦石
1
星屑
20995
在线时间
9336 小时
注册时间
2012-6-19
帖子
7107

开拓者短篇九导演组冠军

2
发表于 2016-9-6 18:07:34 | 只看该作者
def a
  2
end
def b
  p a
  a = 3
  p a
end
b
b
回复 支持 反对

使用道具 举报

Lv3.寻梦者

梦石
0
星屑
1044
在线时间
251 小时
注册时间
2016-9-2
帖子
126
3
 楼主| 发表于 2016-9-6 18:15:02 | 只看该作者

我的天,你想让我疯掉吗
为什么会这样……
等等,又是声明了个临时变量
我差点又歪到可以修改方法上了
一瞬间的心理历程↑
回复 支持 反对

使用道具 举报

Lv4.逐梦者 (版主)

梦石
0
星屑
9497
在线时间
5073 小时
注册时间
2013-6-21
帖子
3580

开拓者贵宾剧作品鉴家

4
发表于 2016-9-6 19:56:07 | 只看该作者
本帖最后由 RyanBern 于 2016-9-7 09:00 编辑

从别的面向对象语言转过来 Ruby 学起来是有点别扭的。建议的做法是少类比,忘掉从前学过的东西,直接从 Ruby 入手。

Ruby 中,'.'后面出现的一般都是方法,很少有“属性”这个说法。这个和 C# 专门引入了 Property 是不太一样的。另外,C# 的 this 和 Ruby 的 self 差的还是有点远。具体的我也不清楚,可以询问@taroxd 来解释。

楼主的代码中不能认为 self.contents 和 contents 相同,也不能认为 self.contents = 和 contents = 相同。
self.contents 和 self.contents= 这两个都是在调用方法,虽然看起来和 C# 中的 Property 极为类似,但是本质上有很大不同。

当你在这里写下 contents 时,Ruby 解释器会先找一下有没有临时变量叫 `contents',如果找不到,则找有没有该类的方法叫 `contents',如果还找不到则报错。

当你在这里写下 contents= 时,这句话实际是在赋值,而写下 self.contents= 时,这句话是在调用本类的 `contents=' 方法,所以效果是截然不同的。

另外,我对此表示疑问:
CSHARP 代码复制
  1. public int X{ get; set; }
  2. public void test(){ X = 5; }

这样真的合法吗?当使用 C# 的 Property 时,this 真的能省略吗?


有点忘了 C# 了,试了一下应该是可以的。

点评

其实想当然真的是我很想改掉的习惯  发表于 2016-9-6 22:02

评分

参与人数 2星屑 +66 收起 理由
kuerlulu + 6 无之书
晴兰 + 60 无之书

查看全部评分

回复 支持 反对

使用道具 举报

Lv3.寻梦者

梦石
0
星屑
1024
在线时间
1389 小时
注册时间
2010-8-9
帖子
3471
5
发表于 2016-9-6 21:38:46 | 只看该作者
本帖最后由 寒冷魔王 于 2016-9-6 21:39 编辑

原先给Chd114神查过一个离奇Bug(见下面的func2)
RUBY 代码复制
  1. class A
  2.   def initialize
  3.     @x = 0
  4.   end
  5.   def x=(e)
  6.     @x = e
  7.   end
  8.   def x
  9.     @x
  10.   end
  11.   # 注:attr_accessor :x 同理
  12.   def func0
  13.     self.x = 5
  14.   end
  15.   def func1
  16.     x = 5
  17.   end
  18.   def func2
  19.     p x
  20.     if false
  21.       x = 5
  22.     end
  23.     p x
  24.   end
  25. end
  26.  
  27. puts("--0--")
  28. a0 = A.new
  29. a0.func0
  30. p a0.x
  31.  
  32. puts("--1--")
  33. a1 = A.new
  34. a1.func1
  35. p a1.x
  36.  
  37. puts("--2--")
  38. a2 = A.new
  39. a2.func2


Ruby语言十分灵活,有很多匪夷所思的陷阱。很多时候需要通过限制灵活性来减少Bug。比如在类内使用self.调用属性方法(如x, x=)。

另外,像这种名称遮掩在其他语言中也很常见吧,比如
CPP 代码复制
  1. class A
  2. {
  3. public:
  4.         void setX(int x) {
  5.                 this->x = x; // 这种赋值的方法如果要同名的话一般加this。
  6.         }
  7. private:
  8.         int x;
  9. };






SRPG on RM 项目研发组 正式成立。目前SRPG·RMVA系统进度88.8%。SMRC Kernel 进度90%
↖(^ω^)↗热烈庆祝~SMRC Ver5.1 SRPG战棋地图移动范围生成脚本正式发布~~
-----------------------------------------------------------------------------------------
SMRC具有高性能、高兼容、定制自由、使用方便的特点。
1.性能,100移动力轻松算出,无压力;
2.兼容,RGSS1-3通吃,效率保证;
3.支持移动形状定制,支持4方位、6方位、正方形或其他任意有移动规律的形状;
4.可以充当高性能寻路来使用。
【链接点此】
-----------------------------------------------------------------------------------------
【2016/01/06更新 | 改版】RM脚本编辑器Gemini
-----------------------------------------------------------------------------------------
回复 支持 反对

使用道具 举报

Lv3.寻梦者

梦石
0
星屑
1044
在线时间
251 小时
注册时间
2016-9-2
帖子
126
6
 楼主| 发表于 2016-9-6 21:53:44 | 只看该作者
RyanBern 发表于 2016-9-6 19:56
从别的面向对象语言转过来 Ruby 学起来是有点别扭的。建议的做法是少类比,忘掉从前学过的东西,直接从 Rub ...

c#中这样当然可以直接使用 X除非声明了另一个X
问题是c#使用临时变量肯定要显示声明
主要是我想当然的认为了
不过是因为之前用了contents直接用了,然后我试了试,发现可以直接使用父类属性(当然所有语言都可以)
我就很疑惑下面的contents为什么要单独添加self.
回复 支持 反对

使用道具 举报

Lv5.捕梦者 (暗夜天使)

只有笨蛋才会看到

梦石
1
星屑
20995
在线时间
9336 小时
注册时间
2012-6-19
帖子
7107

开拓者短篇九导演组冠军

7
发表于 2016-9-6 22:31:01 | 只看该作者
寒冷魔王 发表于 2016-9-6 21:38
原先给Chd114神查过一个离奇Bug(见下面的func2)
class A
  def initialize

实际上,这些并不是陷阱,
我一直认为写ruby代码唯一需要保证的是:不要让ruby去猜你想做什么
只要保证这一点,很多东西根本不会匪夷所思而是非常符合直觉

例如这里的赋值,因为ruby调用方法不需要括号,所以ruby没法区分你执行 a= 是要调用方法还是创建临时变量,这时,加一个 self 显式消除这种含糊不清的命令就是义务,而如果你的命令不会有任何歧义,那么,省略不必要的修饰词则能让代码变得优雅

点评

这时候就应该上Ripper嘛(逃  发表于 2016-9-18 21:36
因为Ruby十分自由,所以加以限制这个任务就落在了Ruby使用者身上。所以我说像m5说的这种人为限制是太自由的锅。。当然你们说得还是很对的。。  发表于 2016-9-7 10:31
好吧,你们说得对。。。我只是想说要保证安全是需要设定限制的,这个限制可以在语言层次上来做,也可以在语言使用者方面来做。  发表于 2016-9-7 10:29
一个语言支持使用中文做变量名,于是你用中文名写了个代码,结果语言因为文件格式是GBK不是utf-8报错了,也是语言支持中文变量名的锅?  发表于 2016-9-7 09:46
CPU不快不是语言的锅  发表于 2016-9-6 23:40
回复 支持 反对

使用道具 举报

Lv3.寻梦者 (版主)

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

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

开拓者贵宾

8
发表于 2016-9-7 09:26:47 | 只看该作者
本帖最后由 taroxd 于 2016-9-7 09:35 编辑
寒冷魔王 发表于 2016-9-6 21:38
原先给Chd114神查过一个离奇Bug(见下面的func2)
class A
  def initialize


调用方法时(self.x = 这种的除外)不需要加 self,也不建议加。
1. 加了 self 就无法调用 private 方法。(同样 self.x = 这是个例外)
2. 保持这个习惯的话,只要出现了 self.xxx 这样显式写出 self 的方法调用,都可以检查一下代码哪里不规范。(默认脚本中主要是 self.def 和 self.class 这种撞关键字的,还有可以省略 self 但没省略的。self.def 可以理解成为了让技能公式更自然,那个 self.class 我实在无法理解为什么要这么做,又撞关键字而且还和 Kernel 里的方法同名……)

正确的做法是:不要用和方法同名的局部变量,除非两者表达完全相同的意思(此时不会造成混淆)。

RUBY 代码复制
  1. # Good
  2. class Game_BattlerBase
  3.   def hp=(hp)
  4.     @hp = hp
  5.     # 如果这里有东西要访问 hp,那么无论 hp 表示 self.hp 还是局部变量 hp,得到的结果都是一样的。
  6.     refresh
  7.   end
  8. end
  9.  
  10. # Bad。参数名就不能命名为 new_exp 或者 value 之类的吗。
  11. class Game_Actor
  12.   def change_exp(exp, show)
  13.     @exp[@class_id] = [exp, 0].max
  14.     last_level = @level
  15.     last_skills = skills
  16.     # 注意下面两行都使用了 self.exp。不熟悉 ruby 的话是不是很容易掉坑里
  17.     level_up while !max_level? && self.exp >= next_level_exp
  18.     level_down while self.exp < current_level_exp
  19.     display_level_up(skills - last_skills) if show && @level > last_level
  20.     refresh
  21.   end
  22. end

点评

像x这种简单的要命的名字如果没有@x替代的话最好还是加个self.x,因为不知道什么时候顺手就写了x把self.x给偷偷替换了。。当然参数名字也会这样。。  发表于 2016-9-8 20:18
嘛,小心驶得万年船。谁知道哪天会心血来潮起了个怪名字然后又来个重名让人欲哭无泪。。  发表于 2016-9-8 20:16
你不用变量名 x 就不会遮掩啊…… self.x = 就这么写,这层里面写了 self.x = 是例外  发表于 2016-9-8 13:33
额。。直接写x我怕名称不小心被遮掩了。。self.x=呢?  发表于 2016-9-8 08:50
A=Struct.new(:x) { def x2; x * 2; end }  发表于 2016-9-7 13:02
回复 支持 反对

使用道具 举报

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

本版积分规则

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

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

GMT+8, 2024-5-4 20:20

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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