【问题标题】:What is best practice in Ruby to avoid misusing assignment "="?Ruby 中避免滥用赋值“=”的最佳实践是什么?
【发布时间】:2011-03-26 14:49:01
【问题描述】:

我忘记了 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 的使用是否会成为我的第二天性,所以欢迎任何有用的提示。

【问题讨论】:

  • 只是好奇,你是从什么语言转向 Ruby 的?
  • Mladen -- 主要是对从 6510 和 IBM 360 汇编到 PL/I、Pascal、C++、Forth 的各种不同的尝试。不是任何方面的专家......通常我真正的工作是成为一名医生。
  • 在我看来,这是您在编程时必须注意的事情之一。 Pascal 使用:= 进行赋值和= 进行比较,解释BASIC 使用= 进行两者,Perl 使用eq 进行字符串比较,== 进行数字比较,= 进行赋值,基本上你必须把它们都放在你的脑海里。这就是为什么注释、编写干净易懂的代码如此重要的原因。重新审视几个月或几年前编写的代码已经够难的了,然后添加一种具有其独特性的不同语言......这足以让你的大脑流行起来。

标签: ruby variable-assignment equals-operator dup


【解决方案1】:

这在像 Ruby 这样的(本质上是命令式的)语言中可能听起来不正统,但我的建议是:通过完全不更新对象来避免附带损害(除非绝对必要);而是创建新的。你付出了一点性能,但你会得到更清晰、更紧凑、更模块化和更容易调试的代码。

http://en.wikipedia.org/wiki/Functional_programming

因此,在您的示例中,只需使用新名称创建一个新字符串:

complete_name = name + title

【讨论】:

  • 换句话说,使用name += title而不是上面的name << title
  • @Mladen。不完全是,如果您采用函数式方法,那么您将创建一个新字符串:name_with_title = name + title。已更新问题以明确说明。
  • +1 用于函数式编程。我希望我的同事能理解这个原则......
  • name += title 确实创建新字符串(但为其分配了相同的名称name)。
  • @Mladen:我知道,但不建议重复使用相同的名称。如您所知,这是 FP 的优点之一,当您看到一个赋值(名称绑定,在严格的函数式语言中)时,您肯定知道它在其所有范围内都将具有相同的值。
【解决方案2】:

只是对 tokland 答案的补充:

函数式方法坚持immutability - 即不更改现有对象,而是在您想要更改原始对象时创建另一个对象。这在某种程度上违背了 Ruby 也带来的面向对象范式(对象在内部保持其状态,可以通过调用其上的方法来更改),因此您必须在两种方法之间进行一些平衡(另一方面,我们受益通过在一种语言中轻松访问多个范例)。

所以,现在要记住三件事:

  1. 了解 Ruby 中的赋值是什么:只是命名一个对象。因此,当您说 y=x 时,您只是在说“我们将另一个名称 y 赋予任何名称为 x 的名称”。
  2. name << title 变异 对象称为name
  3. name += title 获取名为nametitle 的对象,将它们连接成另一个 对象,并分配新的对象名称name。它不会改变任何东西。

【讨论】:

  • 我同意,严格的函数式编程和 Ruby 的经典 OOP 有时很难(或不可能)结合在一起。我个人的规则是尽可能地保持功能;当你需要改变一个对象的状态时,就这样吧。
【解决方案3】:

我也遇到过这样的情况,导致了一个bug,我花了半天时间才弄明白。我基本上做了这样的事情

hash = {....}
filename = object.file_name
hash.each |k, v| {file_name.gsub!(k, v) if file_name.include? k}

这段代码在循环中,在循环中,我希望变量file_name 再次设置为原始值。但是当我执行file_name.gsub! 时,object.file_name 已更改。有两种方法可以解决这个问题。将.gsub! 调用替换为file_name = file_name.gsub 或使用file_name = object.file_name.dup。我选择了第二个选项。

我认为我们应该小心具有!<< 的方法,因为它们会更改它们所作用的原始对象,尤其是在这样的赋值之后。

【讨论】:

  • 函数式方法会更短:hash.inject(object.file_name) { |string, (k, v)| string.gsub(k, v) }
  • tokland——我有兴趣了解更多关于使用函数式方法的知识……我只玩过一点 Haskell。我在哪里可以了解更多关于在 Ruby 中使用它的信息?
  • @tokland..我听说过函数式编程,但从来没有机会真正尝试理解它..是的..那会短得多..
  • @mike,@rubyprince。我写了一篇关于 Ruby 惯用语的文章,其中稍微介绍了函数式构造。我打算用 Ruby 写一些关于 FP 的具体内容,我会在此处发布 URL。现在:code.google.com/p/tokland/wiki/RubyIdioms
【解决方案4】:

一个方法不应该修改一个变量(例如,通过使用移位运算符),除非它的定义说它会修改它。

所以:永远不要在一个方法中修改一个对象,该方法既没有 (a) 创建它,也没有 (b) 记录它修改它。

【讨论】:

    猜你喜欢
    • 2016-12-22
    • 2013-05-07
    • 1970-01-01
    • 2012-10-11
    • 2016-11-25
    • 2019-04-03
    • 1970-01-01
    • 1970-01-01
    • 2012-02-19
    相关资源
    最近更新 更多