【问题标题】:Why are exclamation marks used in Ruby methods?为什么在 Ruby 方法中使用感叹号?
【发布时间】:2010-10-11 08:42:52
【问题描述】:

在 Ruby 中,一些方法有一个问号 (?),它会问像 include? 这样的问题,询问是否包含有问题的对象,然后返回真/假。

但是为什么有些方法有感叹号 (!) 而其他方法没有呢?

什么意思?

【问题讨论】:

  • 同义词:砰、感叹号
  • 接受的答案应更改为stackoverflow.com/a/612653/109618。参见wobblini.net/bang.txtruby-forum.com/topic/176830#773946——“bang 符号意味着”bang 版本比 non bang 版本更危险;小心处理"" -Matz
  • 如果 onlyall bang 方法是危险的,那么 bang 方法将是一个很好的设计选择。遗憾的是它们不是,因此在记忆什么是可变的和不可变的方面变得令人沮丧。

标签: ruby methods naming-conventions immutability


【解决方案1】:

通常,以! 结尾的方法表示该方法将修改它所调用的对象。 Ruby 将这些称为“危险方法”,因为它们会更改其他人可能引用的状态。下面是一个简单的字符串示例:

foo = "A STRING"  # a string called foo
foo.downcase!     # modifies foo itself
puts foo          # prints modified foo

这将输出:

a string

在标准库中,您会在很多地方看到成对的名称相似的方法,一个带有!,一个没有。没有的那些被称为“安全方法”,它们返回原始的副本,并将更改应用于副本,而被调用者保持不变。这是没有! 的相同示例:

foo = "A STRING"    # a string called foo
bar = foo.downcase  # doesn't modify foo; returns a modified string
puts foo            # prints unchanged foo
puts bar            # prints newly created bar

这个输出:

A STRING
a string

请记住,这只是一个约定,但很多 Ruby 类都遵循它。它还可以帮助您跟踪代码中的修改内容。

【讨论】:

  • 要非常小心 - 许多较小的库不遵循这个约定。如果发生奇怪的事情,经常替换 obj.whatever!用 obj=obj.whatever!修复它。非常令人沮丧。
  • bang 也用于在没有方法时引发异常的方法,例如:saveActiveRecord 中的 save!
  • @AbhilashAK save! 如果无法保存,则会引发错误。这与返回 true/false 的常规保存相反。
  • @tgamblin Ruby 中有很多方法可以在没有刘海的情况下进行变异。甚至有一些罕见的方法不会突变,但会做一些令人惊讶的事情,比如引发错误或跳过错误。 Bangs 习惯于说这是该方法的更不寻常的版本,我认为这应该反映在您的答案中,因为它被标记为正确。
  • 哈!我喜欢它。多么疯狂的语言啊。
【解决方案2】:

感叹号意味着很多东西,有时除了“这很危险,小心”之外,你无法从中看出很多。

正如其他人所说,在标准方法中,它通常用于表示导致对象自身发生变异的方法,但并非总是如此。请注意,许多标准方法会更改其接收器并且没有感叹号(popshiftclear),而一些带有感叹号的方法不会更改其接收器(exit!)。例如,请参阅this article

其他库可能会以不同的方式使用它。在 Rails 中,感叹号通常意味着该方法将在失败时抛出异常,而不是静默失败。

这是一种命名约定,但许多人使用它的方式略有不同。在您自己的代码中,一个好的经验法则是在方法执行“危险”操作时使用它,尤其是当存在两个同名方法并且其中一个比另一个更“危险”时。不过,“危险”几乎可以表示任何含义。

【讨论】:

    【解决方案3】:

    此命名约定源自Scheme

    1.3.5 命名约定

    按照惯例,过程的名称 总是返回一个布尔值 通常以“?”结尾。此类程序 称为谓词。

    按照惯例,过程的名称 将值存储到以前 分配的位置(见第 3.4 节) 通常以“!”结尾。此类程序 称为变异程序。经过 约定,由 a 返回的值 未指定突变过程。

    【讨论】:

    • +1 这个答案,因为有一个文档可以对 !用法。非常好的答案史蒂文
    • 感谢@DavidSilveira!
    【解决方案4】:

    !通常意味着该方法作用于对象而不是返回结果。来自书Programming Ruby

    “危险”或修改接收器的方法可能以“!”结尾。

    【讨论】:

      【解决方案5】:

      最准确的说法是用Bang 的方法!是更多的dangeroussurprising 版本。有许多方法可以在没有 Bang 的情况下发生变异,例如 .destroy,并且通常方法只有在核心库中存在更安全的替代方案时才会有 bang。

      例如,在 Array 上我们有 .compact.compact!,这两个方法都会改变数组,但是如果数组中没有 nil,.compact! 返回 nil 而不是 self,这比只返回 self 更令人惊讶.

      我发现的唯一非变异方法是Kernel.exit!,这比.exit 更令人惊讶,因为在进程关闭时您无法捕获SystemExit

      Rails 和 ActiveRecord 延续了这一趋势,因为它使用 bang 来实现更多“令人惊讶”的效果,例如 .create!,它会在失败时引发错误。

      【讨论】:

        【解决方案6】:

        来自themorohoax.com:

        根据我个人的喜好,可以按以下方式使用刘海。

        1) 如果方法不执行,活动记录方法会引发错误 它会说什么。

        2) 活动记录方法保存记录或方法保存记录 对象(例如脱衣舞!)

        3) 一个方法做了一些“额外”的事情,比如发布到某个地方,或者 一些动作。

        重点是:只有在你真正考虑过是否 这是必要的,以节省其他开发人员的烦恼 检查你为什么使用爆炸。

        爆炸为其他开发者提供了两个线索。

        1) 调用后不需要保存对象 方法。

        2) 当你调用方法时,db 会被改变。

        http://www.themomorohoax.com/2009/02/11/when-to-use-a-bang-exclamation-point-after-rails-methods

        【讨论】:

          【解决方案7】:

          简单解释:

          foo = "BEST DAY EVER" #assign a string to variable foo.
          
          => foo.downcase #call method downcase, this is without any exclamation.
          
          "best day ever"  #returns the result in downcase, but no change in value of foo.
          
          => foo #call the variable foo now.
          
          "BEST DAY EVER" #variable is unchanged.
          
          => foo.downcase! #call destructive version.
          
          => foo #call the variable foo now.
          
          "best day ever" #variable has been mutated in place.
          

          但是,如果您曾经在上面的解释中调用过方法downcase!foo 将永久更改为小写。 downcase! 不会返回新的字符串对象,而是将字符串替换到位,将foo 完全更改为小写。 我建议你不要使用downcase!,除非绝对必要。

          【讨论】:

            【解决方案8】:
            !
            

            我喜欢将其视为一种爆炸性的变化,它会摧毁之前发生的一切。 Bang 或感叹号表示您正在对代码进行永久保存的更改。

            如果您使用例如 Ruby 的全局替换方法gsub!您所做的替换是永久性的。

            您可以想象的另一种方式是打开一个文本文件并进行查找和替换,然后保存。 ! 在您的代码中执行相同的操作。

            如果您来自 bash 世界,另一个有用的提醒是 sed -i 具有类似的永久保存更改的效果。

            【讨论】:

              【解决方案9】:

              底线:! 方法只是更改它们被调用的对象的值,而没有! 的方法返回一个操作值,而不会覆盖调用该方法的对象。

              如果您不打算将原始值存储在您调用该方法的变量中,请仅使用!

              我更喜欢这样做:

              foo = "word"
              bar = foo.capitalize
              puts bar
              

              foo = "word"
              puts foo.capitalize
              

              代替

              foo = "word"
              foo.capitalize!
              puts foo
              

              以防万一我想再次访问原始值。

              【讨论】:

              • 因为您的回答对任何方式都没有帮助。 “底线:!方法只是改变了它们被调用的对象的值”是不正确的。
              • @Darwin 它确实改变了对象的值。 ! 改变对象而不是返回修改后的副本。
              • 那么你认为这有什么作用? User.create!
              • 这是一个更好的论据来解释为什么你的答案是完全错误的。阅读 Matz 对 bang“!”的评论。 ruby-forum.com/topic/176830#773946。您是否仍然觉得您的“底线:!方法只是改变了它们被调用的对象的值”在任何方面都是正确的?
              • @Darwin:完全正确?不,我现在明白了。 “无论如何?”是的。修改它的参数,或者调用! 方法的对象,显然是“危险的”——这是不言而喻的;任何改变其任何参数的方法都是危险的,因为您可能会丢失数据。我承认:我被告知! 修改了它的对象,我从来没有真正质疑过这一点。所以我感谢你。
              【解决方案10】:

              称为“破坏性方法”,它们倾向于更改您所指对象的原始副本。

              numbers=[1,0,10,5,8]
              numbers.collect{|n| puts n*2} # would multiply each number by two
              numbers #returns the same original copy
              numbers.collect!{|n| puts n*2} # would multiply each number by two and destructs the original copy from the array
              numbers   # returns [nil,nil,nil,nil,nil]
              

              【讨论】:

                猜你喜欢
                • 2011-05-21
                • 2015-06-19
                • 2019-02-13
                • 2020-05-30
                • 2011-09-09
                • 1970-01-01
                • 2020-05-03
                • 2011-12-22
                相关资源
                最近更新 更多