为什么在Ruby方法中使用感叹号?


540

在Ruby中,某些方法带有一个问号(?),它会问一个问题,例如include?询问是否包含所讨论的对象,然后返回true / false。

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

这是什么意思?


21
同义词:bang,感叹号
prusswan 2012年

17
接受的答案应更改为stackoverflow.com/a/612653/109618。参见wobblini.net/bang.txtruby-forum.com/topic/176830#773946- “爆炸符号表示”爆炸版本比非爆炸版本危险得多;小心处理“”“ -Matz
David J.

2
如果爆炸方法将是一个伟大的设计选择只有所有爆炸方法是危险的。不幸的是,它们不是,因此在记住什么是可变的和不可变的内容时,这成为令人沮丧的练习。
Damien Roche

Answers:


617

通常,以结尾!的方法表示该方法将修改其调用的对象。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类。它还可以帮助您跟踪代码中正在修改的内容。


2
还有退出与退出之类的情况!和(在轨道上)保存与保存!
安德鲁·格林(Edward Grimm)2009年

24
请非常小心-许多较小的库不遵循此约定。如果发生奇怪的事情,请经常替换obj.whatever!与obj = obj.whatever!解决它。非常沮丧。
莎拉·梅

101
bang也用于在没有方法的情况下引发异常的方法,例如:saveand save!inActiveRecord
ecoologic 2011年

3
@AbhilashAK 保存!如果无法保存,则会引发错误。这与常规保存返回true / false相对。
BookOfGreg 2014年

31
@tgamblin Ruby中有许多方法可以变异而不会发生刘海。甚至还有一些罕见的方法,它们不会发生爆炸,但会发生一些令人惊讶的事情,例如引发错误或跳过错误。经常使用Bangs表示这是该方法的更特殊的版本,我认为这应该反映在您的答案中,因为它被标记为正确。
BookOfGreg 2014年

143

感叹号意味着很多事情,有时候除了“这很危险,要小心”之外,您不能从中看出很多。

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

其他库可能会不同地使用它。在Rails中,感叹号通常意味着该方法将在失败时引发异常,而不是无声地失败。

这是一个命名约定,但是许多人以不同的方式使用它。在您自己的代码中,一个好的经验法则是在某个方法执行“危险”操作时使用它,特别是当存在两个具有相同名称的方法并且其中一个方法比另一个方法更“危险”时。“危险”几乎可以指任何东西。


75

此命名约定从Scheme中取消。

1.3.5命名约定

按照约定,总是返回布尔值的过程的名称通常以``?''结尾。这样的过程称为谓词。

按照约定,将值存储到先前分配的位置中的过程的名称(请参见第3.4节)通常以``!''结尾。这样的过程称为突变过程。按照惯例,未指定突变过程返回的值。


2
+1对这个答案,因为有一个文档为给出了合理的解释!用法。真的是个好答案史蒂文
DavidSilveira

谢谢@DavidSilveira!
史蒂芬·休伊格

24

!通常意味着该方法对对象起作用而不是返回结果。从《Ruby编程》一书中:

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


18

最准确地说是用Bang方法!是更危险或更令人惊讶的版本。有许多不带Bang突变的方法,例如.destroy,一般情况下,只有在核心库中存在更安全替代方法的bang才存在。

例如,在Array上,我们有.compactand .compact!方法,这两种方法都会使数组发生变化,但是.compact!如果数组中没有nil ,则返回nil而不是self,这比仅返回self更为令人惊讶。

我发现的唯一非突变方法是Kernel.exit!这比.exit因为SystemExit进程关闭时无法捕获而引起的惊讶更令人惊讶。

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


16

从themomorohoax.com:

根据我的个人喜好,可以按以下方式使用爆炸声。

1)如果活动记录方法未按预期执行操作,则会引发错误。

2)活动记录方法保存记录或方法保存对象(例如,条带!)

3)方法会做一些“额外”的事情,例如张贴到某个地方或执行某些操作。

关键是:只有在真正考虑过是否有必要时才使用爆炸,以免其他开发人员不必检查为什么要使用爆炸。

爆炸为其他开发人员提供了两个提示。

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

2)当您调用该方法时,数据库将被更改。

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


6

简单说明:

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!除非完全必要,否则不要使用。


1
!

我喜欢将其视为具有爆炸性的变化,它摧毁了一切。爆炸或感叹号表示您正在对代码进行永久保存。

例如,如果您使用Ruby的方法进行全局替换,gsub!则所做的替换将是永久的。

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

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


1

称为“破坏性方法”的方法通常会更改您所引用的对象的原始副本。

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]

0

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

!在不打算将原始值存储在调用方法的变量上时使用。

我喜欢做类似的事情:

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

要么

foo = "word"
puts foo.capitalize

代替

foo = "word"
foo.capitalize!
puts foo

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


1
因为您的回答对任何方式都没有帮助。“底线:!方法只是更改它们被调用的对象的值”是不正确的。
达尔文

@Darwin它确实会更改对象的值。!更改对象,而不是返回修改后的副本。
查尔斯

那么您认为这是什么呢?User.create!
达尔文

@Darwin在什么情况下?ActiveRecord?
查尔斯

是的,ActiveRecord。
达尔文
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.