Ruby 一个很容易中招的地方

tumayun · 发布于 2012年08月24日 · 最后由 zw963 回复于 2012年09月09日 · 3577 次阅读
967

一个很容易中招的地方

def self.rule_exchange_anhao(tao_deal)
  find_rule_by_operation_name(operation_name) if operation_name = tao_deal_operation_name(tao_deal)
end

以上写法是错误的,同行if语句的内容部分引用到的变量的话,一定要在用之前就声明,不然会报错的

undefined local variable or method `operation_name' for #<Class:0x8c83d38>

应该改为多行if语句

def self.rule_exchange_anhao(tao_deal)
  if operation_name = tao_deal_operation_name(tao_deal)
    find_rule_by_operation_name(operation_name)
  end
end

刚才中招了

共收到 44 条回复
96

可能解析器会把后置的if表达式提到前面来再执行吧,不过我自己写ruby的代码从来就没用过后置的if表达式,觉得后置的更难读。

77

加个括号应该可以吧

967

@HungYuHei 哪加括号?

a = b if (b = 1.to_s)

这样?

undefined local variable or method `b' for #<Object:0x7fb6b7b242b8>
96

#2楼 @HungYuHei 说的对,加个括号就ok

find_rule_by_operation_name(operation_name) if operation_name = tao_deal_operation_name(tao_deal)​)

是不是优先级问题?

681

cc=ee 是赋值语句,返回永远是true,除非ee的值是nil或false 所以 if(cc=ee) 是错误的, ruby1.9 会有 warning: found = in conditional, should be ==

因该写成 if cc==ee .用两个等于号. 这是语言基础,也就那最基本的10页教程.

967

@zhaoguobin no!加括号没有用的 @sevk cc=ee 是赋值语句,返回永远是true 我觉得这句很有问题 ruby里面返回的是赋值后的值 这里值为nil的话是不会执行下去的...

967

@sevk 我想你应该没有理解我的问题

1031

#7楼 @tumayun

怎么说呢? Ruby虽然灵活, 绝对不是让你这样用的. 你这样的代码, 绝对是未来bug的源泉, 你要是不改, 迟早还要再次中招. 建议有空看看Ruby重构吧.

353

if后面的条件最好不是赋值语句,会有

warning: found = in conditional, should be ==
967

@cantin 纯粹的赋值会有你说的问题,加上其他运算就不会有warning @zw963 我觉得这样的用法没有什么问题,主要是自己的基础知识不过硬引起的,这次遇到了,我下次就会注意。谢谢你的建议,ruby重构看了一部分。

96

谭老湿的风格毁人不倦啊...

410

if后置一般都是做判断,很少在判断语句中写赋值。约定啊。

967

@yakjuly 确实,if后置就应该只做判断

96

我觉得楼主想写出更酷的代码,但反而得不偿失

121

( operation_name = tao_deal_operation_name(tao_deal) ) && find_rule_by_operation_name(operation_name)

如果想少两句代码, 这种方式是比较推荐的. 符合一般理解

967

额,我不是在评价这种写法好不好,只是提醒一下大家。 或者我想多了。。。

1801

@tumayun 恩,不错,学习了,大家只是好心的讨论这样的写法好坏,技术爱好者都是这样 。。。

713

我想应该是范围域不一样

if b = 1
  a = b
end

a = b 中在if 中,而

a = b if b = 1

在范围外,自然不能找到b

8

前几天看到Rubinius的开发者抱怨MRI2.0会在后置条件里赋值的时候强制输出warning: https://twitter.com/brixen/status/233688352552005632

至于还有这么奇怪的差异可能他还没发现,,,大概是MRI里对后置的if/unless处理有所不同。而Rubinius返回的结果是正常的(没有让我惊讶)。

8

#8楼 @zw963 这和重构没关系啊

3432

Ruby這點的確不爽,不過全局的$n可以這麼用還是很給力的。

foo[$1] = $2 if 'name = value' =~ /(.*)\s*=\s*(.*)/
1031

#20楼 @hooopo

怎么没关系呀. 任何一个阅读过重构的人, 看到楼主这样写代码, 不发飙才怪.

重构的基本要素就是要让代码清晰易读, 楼主的代码, 在条件语境中赋值, 是重构强烈不推荐的. 怎么能没有关系呢?

#21楼 @shouya

这个用法很好啊. 很明显, 这个是条件语义, 而非赋值语义.

1031

#18楼 @azhao

其实没必要在这个上面纠结, 如果一定要表达楼主的意思, 可以这样写更清晰:

b = 1 and a = b
594
if operation_name = tao_deal_operation_name(tao_deal)
  #do some thing
end

# 重构如下
operation_name = tao_deal_operation_name(tao_deal)
if operation_name
  #do some thing
end

if 语句中 不应该出现 一个等号。 因为, 一个等号两个等号 看起了 极度相似, 不仔细看,会造成混乱。 这是一个坏习惯。

967

好吧,这种代码是有点坏味道,但是大家还是应当注意一下,不要在小问题上犯错

8

@zw963 @ery

使用=(赋值符号)的返回作为表达式的值是可以的,但是记得在记得在两边加上括号。

# 好的 - 表明了赋值的意图
if (v = array.grep(/foo/)) ...

# 不好的
if v = array.grep(/foo/) ...

# 好的 - 不仅表明了赋值,还表明了正确的优先级
if (v = self.next_value) == "hello" ...

from:http://ruby-china.org/wiki/coding-style

594

#25楼 @tumayun 不论怎样,还是要谢谢你的分享和提醒,谢谢!:)

1031

#26楼 @hooopo

不是不能用, 只是不推荐而已. 我看着还是别扭, 这就好像重构里面讲, 你通过方法参数传递进来一个参数, 然后在方法体内, 又对这个参数重新赋值一样, 这样方法里的参数还是原来的那个参数吗? 你可以这样用, 不过你到底是要传递形参进来, 还是赋值? 而且对于Ruby这种传值而不是传引用的动态语言, 会引起歧义.

有时候多一行代码更清晰一些. 看似小问题, 你可能在几个月后, 浏览代码, 在这个地方栽跟头.

该什么语义就什么语义, 这绝对不会阻碍你写出优质的代码. Wiki上只是说如果非要那么写, 一定加括号, 这是好的风格 (虽然不加括号一样执行正确), 但是并不代表支持那么写.

8

#28楼 @zw963 In-logic assignment 是Ruby Idiom。大家都这么用,甚至流行的code stye也推荐。

多一行代码更清晰一些。。。真不能理解。。。

刚才随便在Rails和Rubinius里找了几行,讨论问题还是要回到现实世界:

https://github.com/rails/rails/blob/master/activerecord/lib/active_record/sanitization.rb#L59 https://github.com/rails/rails/blob/master/activerecord/lib/active_record/reflection.rb#L298 https://github.com/rails/rails/blob/master/activerecord/lib/active_record/nested_attributes.rb#L281 https://github.com/rubinius/rubinius/blob/master/lib/un.rb#L227

594

#28楼 @zw963 我认同你的观点。 这个地方,我栽过几次,不过是其他语言。 程序猿都很固执。 多一行代码更清晰。 非常有道理。 一流的代码是简单明了, 而不是玩弄语法。

8
一流的代码是简单明了,

实在理解不了你的简单明了....

而不是玩弄语法。

这不叫玩语法,这叫入乡随俗。就像函数式语言里用列表解析是习俗,而嵌套循环就是奇葩。

好的代码应该是simple and abstracts repeated patterns (DRY). 我认为 In-logic assignment 做到了。同样的还有 Object.tapObject.tryObject.blank?||=(这个可以叫做memoizable assignment partten?)还有Rails里的 find_or_create方法。

这些都是为了解决实际问题而生的Ruby Idoim。我用着很happy,这是一个好特性。

395

#24楼 @ery 我觉得这种写法比较好,更好懂。if 应该尽量用来做条件判断。

1031

#32楼 @hooopo

呵呵, 你举的例子很好啊. 不过和你之前的说法自相矛盾... 那你说他们毫无例外的都没有加括号, 这是谁错了呢?

如果一定让我两者选其一, 我宁可选择你介绍的代码示例, 反正不推荐的用法都用了, 还加那两个括号干嘛? 只要了解 Ruby 运算符的结合性, 就会知道, 几乎所有情况下, 不加括号的表达式很少出错, 因为一开始Ruby就是为不加括号而设计的.

但是如果让我自己写, 我一定会写两行, 因为写在一起除了可能会让你出错, 没有任何好处可言. 这是个条件表达式, 又不是方法, 或者block, 这样的代码, 在if语句内赋值, 甚至会给我一个错觉, 是不是声明了一个条件表达式内的本地变量?? , 如果不这样写法, 我根本不可能有这样的错觉.

1031

#32楼 @hooopo

我记得之前有篇回帖, 你有介绍如何在 github 上引用一段代码, 不过我没注意看.

再给我介绍下你是怎么做到的? 难道手动输入或者有什么一键点击?

8

stop

3432

#22楼 @zw963 不,其實這個涉及到賦值了,在=~內部,的確有把匹配內容賦給$1的行為。

譬如說,這麼做就不行:

foo[name] = value if 'name = value' =~ /(?<name>.*)\s*=\s*(?<value>.*)/

這個是局部變量作用域的問題因為下面的代碼正常工作:

if 'name = value' =~ /(?<name>.*)\s*=\s*(?<value>.*)/
  foo[name] = value
end
1031

#38楼 @shouya

我看了半天才明白你要表达的意思, 这样的代码说实话, 更乱了. 第一行之所以不工作, 是因为先执行的 name = value 的缘故吧. 如果是从右往左执行, 就可以了.

1031

#37楼 @hooopo

stop是啥意思? 是回答我上面的那个问题吗?

96

#40楼 @zw963 直接点击左边行号然后复制地址栏的地址,比如在pentadactyl里直接点行号然后yy一下就OK了

1031

#41楼 @aptx4869

知道了. 谢了.

3432

#39楼 @zw963 呃,這個… 其實我還是沒有明白你的意思,'name = value'不是字串嗎怎麼會被執行?

1031

#43楼 @shouya

嗨~ 我又看了下咱俩的回帖, 其实是我没明白你的意思. 惭愧呀~

其实你一直在说一个方面, 而我以为你一直在说另一个方面. 哈哈.

我刚刚想了想, 确实蛮有趣的.

foo[name] = value if 'name = value' =~ /(?<name>.*)\s*=\s*(?<value>.*)/

这个之所以无法实现, 其实因为: 在使用一个本地变量之前, 这个本地变量必须首先被定义. 而本地变量的定义是在源码解析的过程中完成的. 这就和下面的代码效果类似:

if false
  x = 0
end
p x    # => x = nil

在语法解析阶段, 在使用本地变量 x 之前, 解释器看到了 x = 0 这个赋值语句, 首先初始化 x这个变量为 nil ,虽然稍后没有具体执行 x = 0 对 x 进行赋值, 但是这个 x 已经被初始化了.

上面那个正则匹配的示例, 有点类似于下面这个示例:

p x if x = 0   # = > undefined local variable or method `x' for main:Object

之所以出错, 是因为在源码解析阶段, 运行p x 的时候, 这个 x 是不存在的. 只有在运行 x = 0 之后, x 才是存在的. 所以这个语句在执行阶段, 看起来应该是这样的: p x if 0

而后面的有关$1,$2的那个示例, 则不一样:

foo[$1] = $2 if 'name = value' =~ /(.*)\s*=\s*(.*)/

诸如 $1, $2 这些变量, 在解释器一开始的时候, 就已经被定义并初始化为 ni l . 然后在源码解析时, 括号内的结果直接被赋值给之前定义的 $1, $2, 所以在执行时, $1, $2 就是可用的.

以上结论都是猜的, 因为不懂 C, 也没办法验证, 不妨请出 @skandhas , 能否单独开贴, 或者在这里, 给大概解释下, Ruby 解释器在解释一个 .rb 文件时, 大概过程分为几个大的步骤.

BTW: @skandhas, 我发现可能我邮箱有些问题, 给好些个人发邮件都没回复, 我前阵子给你推荐了个Emacs插件, 发到你163邮箱了, 你是不是没收到?

8 hooopo Is In-logic assignment Bad? 中提及了此贴 07月12日 13:44
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册