【问题标题】:Meaning of Ruby methods ending with "!"以“!”结尾的 Ruby 方法的含义
【发布时间】:2021-11-08 10:19:46
【问题描述】:

我是 Ruby 的新手,到目前为止发现! 的解释有点过于技术化。

假设我们有以下内容:

print "What is your first name?"

first_name = gets.chomp.upcase!

puts "Hi, #{first_name}. How are you?"

! 为方法添加了什么?这是否意味着每次我在任何后续字符串中使用#{first_name} 时,名称中的所有字母都会以大写形式打印?如果我离开!,代码会受到怎样的影响?

如果可以的话,请用通俗易懂的方式解释它,因为我仍在学习 Ruby 的一些技术术语。

提前致谢。

【问题讨论】:

  • 嗨史蒂夫。到目前为止,您对 bang 方法有什么了解?我认为知道这将有助于我们以不同的方式回答,也许会找到适合您的解释。
  • upcase 返回一个 new 大写字符串,而 upcase! 就地更改其接收器。如果字符串已经是大写,! 方法也会返回 nil。在这种情况下,您最终会得到first_namenil。因此,您应该坚持使用非爆炸变体。
  • 爆炸方法?这是我第一次听到与 Ruby 相关的“砰”字。我正在学习 codeacademy.com 上的 Intro to Ruby 课程,十天前我什至对 Ruby 一无所知。像“...upcase!就地更改其接收器”这样的短语对我来说是一种拉丁语(“就地接收器?”)。这就是为什么我现在正在寻找一个简化的解释,直到我更熟悉 Ruby 的行话。谢谢。
  • @Steve:见this style guide
  • @Steve Calling MethodsMethods 的文档值得一读。它们解释了 Ruby 的方法相关概念和约定,包括“接收器”和“爆炸方法”等术语。

标签: ruby string methods string-interpolation


【解决方案1】:

Ruby 方法名称可以以! 结尾。它本身并没有做任何特别的事情,但是有一个关于哪些方法拥有它的约定(尽管它并不总是一致的);因此,只需逐个方法学习String#upcase! 方法(以及它与String#upcase 的区别),约定是一个有用的提示。

惯例是,以! 结尾的方法要么是危险的,要么会更改对象,要么执行其他类型的更改,或者会引发其他版本不会的错误。

具体来说,String#upcase! 更改了它所操作的字符串。 String#upcase 生成一个新的字符串。

foo = "test"
p(foo.upcase)  # => "TEST"
p(foo)         # => "test"

foo = "test"
p(foo.upcase!) # => "TEST"
p(foo)         # => "TEST"    ​

如 cmets 中所述,String#upcase! 在不需要进行向上转换时返回 nil,因此您永远不想将 String#upcase! 的返回值重新分配给您尝试分配给 upcase! 的同一变量。

foo = "TEST"
p(foo.upcase!) # => nil
p(foo)         # => "TEST"    ​

RE:爆炸方法:see here

编辑:“receiver”是接收消息的对象(即方法调用)。在foo.upcase! 中,foo 中的对象是接收者。在"TEST".upcase 中,字符串"TEST" 是接收者。

“in-place”` 表示对象本身已更改。另一种方法是创建一个具有您想要应用的更改的新对象,而不更改原始对象。例如,如果你有一张照片,用一张纸盖住下半部分并拍照,你通过创建一张缺少下半部分的新照片进行了粗略的审查(但原始照片仍然存在)。如果你在原始照片上泼漆,你就“就地”审查了它。

【讨论】:

  • “破坏性”破坏了原始内容,因此基本上是“就地更改”(或突变)的同义词。 “非破坏性”方式是“制作新对象”方式。
  • 谢谢。如果您为我解析以下内容,将会很有帮助。这是关于的解释!和破坏性的方法,但我仍在努力掌握术语。这句话是这样的:“在方法名称中使用 ! 只是改变其调用对象和/或传递给方法的一个或多个参数的方法的约定。”让我们使用“name = gets.chomp.capitalize!”。什么是“调用对象”?传递给该方法的“参数”是什么?我理解命令行界面中的参数,但直到今天才在 Ruby 中遇到这个术语。谢谢。
  • 如前所述,name = gets.chomp.capitalize! 是错误代码;使用name = gets.chomp.capitalizename = gets; name.chomp!; name.capitalize!。第一种情况下的接收者是gets返回的字符串;在第二种情况下,name 变量的内容。在这段代码中,没有参数(也称为参数);但你可以写"it".capitalize(:turkic),其中"it" 是接收器,:turkic 是单个参数。另外,由于 Ruby 不强制使用括号,puts "hi" 基本上等同于 puts("hi") - "hi" 是一个参数。
  • 可能值得注意的是,有一些核心方法可以改变接收器,这些方法不是“bang”方法(即,其名称不以感叹号结尾)。一些例子是Array#<<Array#clearArray#replaceString#replaceHash#replace
  • Matz's quote 关于这些方法:“bang (!) 不表示“破坏性”,也没有表示非破坏性。bang 符号表示“bang 版本是比它的非爆炸对应物更危险;小心处理”。由于 Ruby 有很多“破坏性”的方法,如果按照你的想法 bang 符号,每个 Ruby 程序都会充满 bang,因此很丑。”
【解决方案2】:

! 在方法中添加了什么?

什么都没有。 ! 只是消息名称的一部分,就像 upcase 一样。它不会改变消息的任何内容。

如果我离开! 对代码有何影响?

那么你只是发送了一条不同的消息。

upcaseupcase! 之间或 foobar 之间绝对没有任何区别。它们只是消息名称。如果你写foo,那么你发送消息foo,如果你写bar,那么你发送消息bar,如果你写upcase,那么你发送消息upcase(在你的情况下将被分派到方法String#upcase),如果你写upcase!,那么你发送消息upcase!(在你的情况下,它将被分派到方法String#upcase!)。

! 结尾的消息名称绝对没有什么特别之处,就像以? 结尾的消息名称绝对没有什么特别之处一样,就像以a 或@ 结尾的消息名称绝对没有什么特别之处一样987654351@或c

,但是,有一些约定。特别是,约定是:

如果您有 一对 的方法,它们都或多或少地做同样的事情,那么更令人惊讶的方法被命名为带有!

就是这样。仅此而已。

例如,有Process::exitProcess::exit! 方法。它们都做同样的事情(终止进程),但其中一个跳过运行已安装的退出处理程序

由于它们都做同样的事情,它们都可以被命名为“exit”,但是当然,你不能有两个同名的方法(在同一个接收器上)。因此,我们需要以某种方式区分两者。我们可以为两者中的一个取一个完全不同的名称,但这也很烦人。

因此,我们将! 添加到这两种方法之一。现在,问题是:我们将! 添加到两者中的哪一个?根据我们的约定,“更令人惊讶”的应该是!。 Ruby 开发人员选择了忽略退出处理程序的方法,我认为这是正确的选择:退出处理程序的全部意义在于它们在您退出时运行,因此退出而不运行退出处理程序的方法令人惊讶,因此应标有!

请注意,没有与“破坏性”或“突变”有关。 Process::exit! 不会改变任何东西,也不会破坏任何东西。另请注意,我们使用! 来标记方法对更令人惊讶的方法。当没有名为 foo 的方法时,永远不应该有名为 foo! 的方法。

特别是,一个方法没有!这一事实并不能告诉你任何关于该方法的信息。 确实具有!的方法唯一告诉您的是有两种方法,您可能应该查看文档以确保您了解这两种方法的作用和作用“惊喜”是! 正在警告您。就是这样。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-15
    • 1970-01-01
    • 1970-01-01
    • 2015-09-13
    • 2017-08-24
    • 1970-01-01
    • 2011-05-16
    • 1970-01-01
    相关资源
    最近更新 更多