Ruby 中避免误用赋值“=”的最佳实践是什么?
我已经被咬了好几次了,因为我忘记了 Ruby 中的 x = y 使 x 与 y 引用同一个对象;我太习惯用 Ruby 术语表示的语言了,x = y.dup
。忘记了这一点,当我认为在作业的右侧是安全的时,我无意中更改了 y 。
我可以看到,如果没有特殊原因,避免简单的 x = y
赋值是有意义的,但同样的事情可能潜伏在其他地方,例如
name = (person.last_name.blank? ? 'unknown' : person.last_name)
稍后的 name <<< title
实际上会改变 person.last_name 而不仅仅是名字。
如果这种情况也发生在你身上,你是如何学会避免它的呢?是否有某些危险信号或模式需要寻找?你是否用怀疑的眼光看待你所做的每项任务?您经常使用 .dup
吗?我不知道 Ruby 的使用是否会成为我的第二天性,所以欢迎任何有用的提示。
I've been bitten a couple of times by forgetting that x = y
in Ruby makes x refer to the same object as y; I'm too used to languages where it means, in Ruby terms, x = y.dup
. Forgetting this, I inadvertently change y
when I think it's safe on the right side of the assignment.
I can see that it would make sense to avoid simple x = y
assignments without a special reason, but the same thing can be lurking in other places such as
name = (person.last_name.blank? ? 'unknown' : person.last_name)
where a later name << title
would actually be changing person.last_name and not just name.
If this has happened to you, too, how have you learned to avoid it? Are there certain red flags or patterns to look for? Do you look with suspicion at each assignment you make? Do you use .dup
a lot? I don't know if Ruby's usage will ever become second nature to me, so any useful tips would be welcome.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
对于像 Ruby 这样的(本质上是命令式的)语言来说,这可能听起来不正统,但我的建议是:通过根本不更新对象来避免附带损害(除非绝对必要);而是创建新的。你付出了一点性能,但你会得到更清晰、更紧凑、更模块化、更容易调试的代码。
http://en.wikipedia.org/wiki/Functional_programming
因此,在您的示例中,只需创建具有新名称的新字符串:
This may sound unorthodox in a (essentially imperative) language like Ruby, but my advice is: avoid collateral damages by not updating objects at all (except when strictly necessary); create new ones instead. You pay a bit of performance but you'll get code which is clearer, more compact, more modular and easier to debug.
http://en.wikipedia.org/wiki/Functional_programming
So, in your example, just create a new string with a new name:
只是对 tokland 答案的补充:
函数式方法坚持 不变性 - 即不改变现有对象,而是创建每当你想改变原来的时候就换另一个。这在某种程度上违背了 Ruby 带来的面向对象范例(对象在内部保留其状态,可以通过调用其方法来更改状态),因此您必须在两种方法之间进行一些平衡(另一方面,我们受益于通过用单一语言轻松访问多个范式)。
所以,现在要记住三件事:
y=x
时,您只是在说“我们为名为x
的事物赋予另一个名称y
”。姓名<< title
变异名为name
的对象。name += title
获取名为name
和title
的对象,将它们连接到另一个对象中,并分配该新对象名称名称
。它不会改变任何东西。Just an addition to tokland's answer:
Functional approach insists on immutability - i.e. not altering existing objects, but creating another whenever you want to change the original one. This is somewhat against the object-orientated paradigm that Ruby also brings (objects keep their state internally, which can be altered by calling methods on it), so you have to balance a bit between the two approaches (on the other hand, we benefit by having multiple paradigms easily accessible in a single language).
So, three things to remember for now:
y=x
, you are only saying "we give another namey
to whatever was namedx
".name << title
mutates object calledname
.name += title
takes objects namedname
andtitle
, concatenates them into another object, and assigns that new object namename
. It doesn't mutate anything.我也遇到过这样的情况,导致了bug,搞了半天才搞清楚。我基本上做了这样的事情
这段代码位于一个循环内,在循环中,我希望变量
file_name
再次设置为原始值。但当我执行file_name.gsub!
时,object.file_name 已更改。有两种方法可以解决这个问题。将.gsub!
调用替换为file_name = file_name.gsub
或执行file_name = object.file_name.dup
。我选择了第二个选项。我认为我们应该小心使用
!
和<<
的方法,因为它们会更改它们所作用的原始对象,尤其是在这样的赋值之后。I also came across such a situation and it resulted in a bug, which I took half a day to figure out. I essentially did something like this
This code was inside a loop and in the loop, I expected the variable
file_name
to be again set to original value. But the object.file_name was changed, as I was performingfile_name.gsub!
. There are 2 ways to solve this. Either replace the.gsub!
call withfile_name = file_name.gsub
or dofile_name = object.file_name.dup
. I opted for the second option.I think we should be careful with methods having
!
and<<
, as they change the original object on which they are acting, especially after assignments like this.方法不应修改变量(例如通过使用移位运算符),除非其定义表明它将修改它。
因此:永远不要在没有 (a) 创建对象或 (b) 没有记录它修改对象的方法中修改对象。
A method should not modify a variable (e.g. by using the shift operator) unless its definition says it will modify it.
So: never modify an object in a method that didn't either (a) create it or (b) document that it modifies it.