Tại sao dấu chấm than được sử dụng trong phương pháp Ruby?


540

Trong Ruby, một số phương thức có dấu chấm hỏi ( ?) hỏi một câu hỏi như thế include?hỏi xem có bao gồm đối tượng trong câu hỏi không, điều này sẽ trả về giá trị đúng / sai.

Nhưng tại sao một số phương pháp có dấu chấm than ( !) trong khi các phương thức khác không có?

Nó có nghĩa là gì?


21
từ đồng nghĩa: bang, dấu chấm than
prusswan

17
Câu trả lời được chấp nhận nên được thay đổi thành stackoverflow.com/a/612653/109618 . Xem wobblini.net/bang.txtruby-forum.com/topic/176830#773946 - "Dấu hiệu tiếng nổ có nghĩa là" phiên bản tiếng nổ nguy hiểm hơn phiên bản không nổ của nó; xử lý cẩn thận "" -Matz
David J.

2
Phương pháp bang sẽ là một lựa chọn thiết kế tuyệt vời nếu chỉtất cả các phương pháp bang đều nguy hiểm. Đáng buồn thay, chúng không phải, và vì vậy nó trở thành một bài tập bực bội trong việc ghi nhớ những gì và không thể thay đổi.
Damien Roche

Câu trả lời:


617

Nói chung, các phương thức kết thúc !chỉ ra rằng phương thức sẽ sửa đổi đối tượng mà nó được gọi . Ruby gọi đây là " phương pháp nguy hiểm " vì chúng thay đổi trạng thái mà người khác có thể có tham chiếu đến. Đây là một ví dụ đơn giản cho chuỗi:

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

Điều này sẽ xuất ra:

a string

Trong các thư viện tiêu chuẩn, có rất nhiều nơi bạn sẽ thấy các cặp phương thức được đặt tên tương tự nhau, một nơi có !và không có. Những cái không có được gọi là "phương pháp an toàn" và chúng trả về một bản sao của bản gốc với những thay đổi được áp dụng cho bản sao , với callee không thay đổi. Đây là ví dụ tương tự mà không có !:

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

Kết quả này:

A STRING
a string

Hãy nhớ rằng đây chỉ là một quy ước, nhưng rất nhiều lớp Ruby tuân theo nó. Nó cũng giúp bạn theo dõi những gì được sửa đổi trong mã của bạn.


2
Cũng có trường hợp như thoát so với thoát! và (trong đường ray) tiết kiệm so với tiết kiệm!
Andrew Grimm

24
Hãy cẩn thận - nhiều thư viện nhỏ hơn không tuân theo quy ước này. Nếu những điều kỳ lạ đang xảy ra, thường thay thế obj.whthing! với obj = obj.whthing! sửa nó Rất bực bội.
Sarah Mei

101
bang cũng được sử dụng cho các phương thức đưa ra một ngoại lệ khi phương thức không có, ví dụ: savesave!trongActiveRecord
ecoologic

3
@ AbhilashAK lưu lại! gây ra lỗi nếu nó không thể lưu Điều này trái ngược với lưu thường xuyên trả về đúng / sai.
BookOfGreg

31
@tgamblin Có rất nhiều phương pháp trong Ruby đột biến mà không có tiếng nổ. Thậm chí có những phương pháp hiếm hoi không đột biến VỚI một tiếng nổ nhưng làm một điều đáng ngạc nhiên như tăng lỗi hoặc bỏ qua lỗi. Bangs được sử dụng để nói rằng đây là phiên bản khác thường hơn của phương pháp và tôi nghĩ rằng điều này nên được phản ánh trong câu trả lời của bạn vì nó được đánh dấu là chính xác.
BookOfGreg

143

Dấu chấm than có nghĩa là nhiều điều, và đôi khi bạn không thể nói nhiều từ nó ngoài "điều này nguy hiểm, hãy cẩn thận".

Như những người khác đã nói, trong các phương thức tiêu chuẩn, nó thường được sử dụng để chỉ ra một phương thức khiến một đối tượng tự biến đổi, nhưng không phải lúc nào cũng vậy. Lưu ý rằng nhiều phương pháp chuẩn thay đổi nhận của họ và không có dấu chấm than ( pop, shift, clear), và một số phương pháp với dấu chấm than không thay đổi nhận của họ ( exit!). Xem bài viết này cho ví dụ.

Các thư viện khác có thể sử dụng nó khác nhau. Trong Rails, một dấu chấm than thường có nghĩa là phương thức sẽ đưa ra một ngoại lệ đối với thất bại thay vì thất bại trong âm thầm.

Đó là một quy ước đặt tên nhưng nhiều người sử dụng nó theo những cách khác nhau. Trong mã của riêng bạn, một quy tắc tốt là sử dụng nó bất cứ khi nào một phương thức đang làm điều gì đó "nguy hiểm", đặc biệt là khi hai phương thức có cùng tên tồn tại và một trong số chúng "nguy hiểm" hơn phương thức kia. "Nguy hiểm" có thể có nghĩa là gần như bất cứ điều gì mặc dù.


75

Quy ước đặt tên này được dỡ bỏ khỏi Đề án .

1.3.5 Quy ước đặt tên

Theo quy ước, tên của các thủ tục luôn trả về giá trị boolean thường kết thúc bằng ``? ''. Thủ tục như vậy được gọi là vị ngữ.

Theo quy ước, tên của các thủ tục lưu trữ giá trị vào các vị trí được phân bổ trước đó (xem phần 3.4) thường kết thúc bằng ``! ''. Thủ tục như vậy được gọi là thủ tục đột biến. Theo quy ước, giá trị được trả về bởi một thủ tục đột biến là không xác định.


2
+1 cho câu trả lời này vì có một tài liệu đưa ra lời giải thích hợp lý cho! sử dụng. Câu trả lời thực sự tốt Steven
DavidSilveira 9/12/2016

Cảm ơn @DavidSilveira!
Steven Huwig

24

! thông thường có nghĩa là phương thức tác động lên đối tượng thay vì trả về kết quả. Từ cuốn sách Lập trình Ruby :

Các phương thức "nguy hiểm" hoặc sửa đổi máy thu, có thể được đặt tên bằng dấu "!".


18

Chính xác nhất là nói rằng các phương thức với Bang! là phiên bản nguy hiểm hơn hoặc đáng ngạc nhiên . Có nhiều phương thức đột biến mà không có Bang như .destroyvà trong các phương thức chung chỉ có tiếng nổ trong đó có một sự thay thế an toàn hơn tồn tại trong lib lõi.

Chẳng hạn, trên Array chúng ta có .compact.compact!, cả hai phương thức đều biến đổi mảng, nhưng .compact!trả về nil thay vì self nếu không có nil trong mảng, điều này đáng ngạc nhiên hơn là chỉ tự trả về.

Phương pháp duy nhất không biến đổi tôi đã tìm thấy với một tiếng nổ là Kernel's .exit!được nhiều ngạc nhiên hơn .exitvì bạn không thể nắm bắt SystemExittrong khi quá trình này được đóng cửa.

Rails và ActiveRecord tiếp tục xu hướng này ở chỗ nó sử dụng tiếng nổ cho các hiệu ứng 'đáng ngạc nhiên' hơn như .create!làm tăng lỗi khi thất bại.


16

Từ themomorohoax.com:

Một tiếng nổ có thể được sử dụng theo những cách dưới đây, theo thứ tự sở thích cá nhân của tôi.

1) Phương thức bản ghi hoạt động sẽ phát sinh lỗi nếu phương thức không thực hiện đúng như ý muốn.

2) Phương thức ghi hoạt động lưu bản ghi hoặc phương thức lưu đối tượng (ví dụ: dải!)

3) Một phương thức thực hiện một số thứ khác, thêm vào đó, như các bài đăng ở một nơi nào đó hoặc thực hiện một số hành động.

Vấn đề là: chỉ sử dụng tiếng nổ khi bạn thực sự nghĩ về việc có cần thiết hay không, để cứu các nhà phát triển khác sự phiền toái khi phải kiểm tra lý do tại sao bạn sử dụng tiếng nổ.

Bang cung cấp hai tín hiệu cho các nhà phát triển khác.

1) rằng không cần thiết phải lưu đối tượng sau khi gọi phương thức.

2) khi bạn gọi phương thức, db sẽ được thay đổi.

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


6

Giải thích đơn giản:

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.

Nhưng nếu bạn từng gọi một phương thức downcase!trong phần giải thích ở trên, foosẽ thay đổi thành chữ thường. downcase!sẽ không trả về một đối tượng chuỗi mới mà thay thế chuỗi tại chỗ, thay đổi hoàn toàn thành chữ thường foo. Tôi đề nghị bạn không sử dụng downcase!trừ khi nó hoàn toàn cần thiết.


1
!

Tôi thích nghĩ về điều này như một sự thay đổi bùng nổ phá hủy tất cả những gì đã đi trước nó. Dấu hoặc dấu chấm than có nghĩa là bạn đang thực hiện thay đổi được lưu vĩnh viễn trong mã của mình.

Nếu bạn sử dụng phương pháp của Ruby để thay thế toàn cầu gsub!thì sự thay thế bạn thực hiện là vĩnh viễn.

Một cách khác bạn có thể tưởng tượng nó, là mở một tệp văn bản và thực hiện tìm và thay thế, sau đó là lưu. !không giống nhau trong mã của bạn.

Một lời nhắc hữu ích khác nếu bạn đến từ thế giới bash là sed -icó tác dụng tương tự làm thay đổi lưu vĩnh viễn.


1

Được gọi là "Phương pháp phá hủy" Họ có xu hướng thay đổi bản sao gốc của đối tượng mà bạn đang đề cập.

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

Dòng dưới cùng: !các phương thức chỉ thay đổi giá trị của đối tượng mà chúng được gọi, trong khi đó một phương thức không !trả về giá trị thao tác mà không ghi lên đối tượng mà phương thức được gọi.

Chỉ sử dụng !nếu bạn không có kế hoạch cần giá trị ban đầu được lưu trữ tại biến bạn gọi là phương thức trên.

Tôi thích làm một cái gì đó như:

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

HOẶC LÀ

foo = "word"
puts foo.capitalize

Thay vì

foo = "word"
foo.capitalize!
puts foo

Chỉ trong trường hợp tôi muốn truy cập lại giá trị ban đầu.


1
Bởi vì câu trả lời của bạn không hữu ích trong bất kỳ cách nào. "Dòng dưới cùng :! Các phương thức chỉ thay đổi giá trị của đối tượng mà chúng được gọi" là không đúng.
Darwin

@Darwin nó không thay đổi giá trị của đối tượng. !làm thay đổi đối tượng thay vì trả về một bản sao đã sửa đổi.
Charles

Vì vậy, bạn nghĩ gì về điều này? User.create!
Darwin

@Darwin trong bối cảnh nào? ActiveRecord?
Charles

Vâng, ActiveRecord.
Darwin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.