Mengapa tanda seru digunakan dalam metode Ruby?

540

Di Ruby beberapa metode memiliki tanda tanya ( ?) yang menanyakan pertanyaan seperti include?itu menanyakan apakah objek yang dimaksud disertakan, ini kemudian mengembalikan true / false.

Tetapi mengapa beberapa metode memiliki tanda seru ( !) di mana yang lain tidak?

Apa artinya?

Lennie
sumber
21
sinonim: bang, tanda seru
prusswan
17
Jawaban yang diterima harus diubah ke stackoverflow.com/a/612653/109618 . Lihat wobblini.net/bang.txt dan ruby-forum.com/topic/176830#773946 - "Tanda bang berarti" versi bang lebih berbahaya daripada versi non bang; tangani dengan hati-hati "" -Matz
David J.
2
Metode bang akan menjadi pilihan desain yang bagus jika saja dan semua metode bang itu berbahaya. Sayangnya mereka tidak, dan itu menjadi latihan frustasi dalam menghafal apa yang bisa dan tidak bisa berubah.
Damien Roche

Jawaban:

617

Secara umum, metode yang berakhir !menunjukkan bahwa metode akan memodifikasi objek yang dipanggil . Ruby menyebut ini sebagai " metode berbahaya " karena mereka mengubah keadaan yang mungkin menjadi referensi orang lain. Berikut ini contoh sederhana untuk string:

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

Ini akan menampilkan:

a string

Di perpustakaan standar, ada banyak tempat Anda akan melihat pasangan metode yang bernama sama, satu dengan !dan yang tanpa. Yang tanpa disebut "metode aman", dan mereka mengembalikan salinan aslinya dengan perubahan diterapkan pada salinan , dengan callee tidak berubah. Inilah contoh yang sama tanpa !:

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

Output ini:

A STRING
a string

Ingatlah ini hanya sebuah konvensi, tetapi banyak kelas Ruby mengikutinya. Ini juga membantu Anda melacak apa yang akan dimodifikasi dalam kode Anda.

Todd Gamblin
sumber
2
Ada juga kasus seperti keluar versus keluar! dan (dalam rel) save versus save!
Andrew Grimm
24
Berhati-hatilah - banyak perpustakaan kecil tidak mengikuti konvensi ini. Jika hal-hal aneh terjadi, seringlah mengganti objek. Apa pun! dengan obj = obj.apapun! memperbaikinya. Sangat membuat frustrasi.
Sarah Mei
101
Bang juga digunakan untuk metode yang menimbulkan pengecualian ketika metode tanpa tidak, misalnya: savedan save!inActiveRecord
ecoologic
3
@AbhilashAK simpan! memunculkan kesalahan jika tidak bisa menyimpan. Ini bertentangan dengan penyimpanan reguler yang mengembalikan benar / salah.
BookOfGreg
31
@tgamblin Ada banyak metode di Ruby yang bermutasi tanpa poni. Bahkan ada metode langka yang tidak bermutasi DENGAN bang tetapi melakukan sesuatu yang mengejutkan seperti meningkatkan kesalahan atau melewatkan kesalahan. Poni biasanya mengatakan ini adalah versi yang lebih tidak biasa dari metode ini dan saya pikir ini harus tercermin dalam jawaban Anda karena ini ditandai sebagai benar.
BookOfGreg
143

Tanda seru berarti banyak hal, dan kadang-kadang Anda tidak bisa mengatakan banyak hal darinya selain "ini berbahaya, hati-hati".

Seperti yang orang lain katakan, dalam metode standar sering digunakan untuk menunjukkan metode yang menyebabkan objek bermutasi, tetapi tidak selalu. Perhatikan bahwa banyak metode standar mengubah receiver mereka dan tidak memiliki tanda seru ( pop, shift, clear), dan beberapa metode dengan tanda seru tidak mengubah penerima mereka ( exit!). Lihat artikel ini misalnya.

Perpustakaan lain mungkin menggunakannya secara berbeda. Dalam Rails, tanda seru seringkali berarti bahwa metode ini akan mengeluarkan pengecualian pada kegagalan daripada gagal secara diam-diam.

Ini adalah konvensi penamaan tetapi banyak orang menggunakannya dengan cara yang sedikit berbeda. Dalam kode Anda sendiri, aturan praktis yang baik adalah menggunakannya setiap kali suatu metode melakukan sesuatu yang "berbahaya", terutama ketika dua metode dengan nama yang sama ada dan salah satunya lebih "berbahaya" daripada yang lain. "Berbahaya" bisa berarti hampir apa saja.

Brian Carper
sumber
75

Konvensi penamaan ini diangkat dari Skema .

1.3.5 Konvensi penamaan

Dengan konvensi, nama prosedur yang selalu mengembalikan nilai boolean biasanya diakhiri dengan ``? ''. Prosedur semacam itu disebut predikat.

Berdasarkan konvensi, nama prosedur yang menyimpan nilai ke lokasi yang sebelumnya dialokasikan (lihat bagian 3.4) biasanya diakhiri dengan ``! ''. Prosedur semacam itu disebut prosedur mutasi. Dengan konvensi, nilai yang dikembalikan oleh prosedur mutasi tidak ditentukan.

Steven Huwig
sumber
2
Memberi +1 pada jawaban ini karena memiliki dokumentasi yang memberikan penjelasan yang masuk akal untuk! pemakaian. Jawaban yang sangat bagus Steven
DavidSilveira
Terima kasih @ DavidSilveira!
Steven Huwig
24

! biasanya berarti bahwa metode bertindak atas objek alih-alih mengembalikan hasil. Dari buku Ruby Pemrograman :

Metode yang "berbahaya," atau memodifikasi penerima, dapat dinamai dengan trailing "!".

pesto
sumber
18

Paling akurat untuk mengatakan bahwa metode dengan Bang! adalah versi yang lebih berbahaya atau mengejutkan . Ada banyak metode yang bermutasi tanpa Bang seperti .destroydan dalam metode umum hanya memiliki poni di mana alternatif yang lebih aman ada di inti lib.

Sebagai contoh, pada Array yang kita miliki .compactdan .compact!, kedua metode memutasi array, tetapi .compact!mengembalikan nihil bukan diri jika tidak ada nol dalam array, yang lebih mengejutkan daripada hanya mengembalikan diri.

Metode-satunya non-bermutasi saya telah menemukan dengan bang adalah Kernel's .exit!yang lebih mengejutkan daripada .exitkarena Anda tidak dapat menangkap SystemExitsedangkan proses adalah penutupan.

Rails dan ActiveRecord melanjutkan tren ini karena menggunakan bang untuk efek .create!yang lebih 'mengejutkan' seperti yang meningkatkan kesalahan pada kegagalan.

BookOfGreg
sumber
16

Dari themomorohoax.com:

Ledakan dapat digunakan dengan cara di bawah ini, sesuai dengan preferensi pribadi saya.

1) Metode rekaman aktif memunculkan kesalahan jika metode tidak melakukan apa yang dikatakannya akan.

2) Metode rekaman aktif menyimpan catatan atau metode menyimpan objek (mis. Strip!)

3) Suatu metode melakukan sesuatu yang "ekstra", seperti posting ke suatu tempat, atau melakukan beberapa tindakan.

Intinya adalah: gunakan bang saja saat Anda benar-benar berpikir apakah perlu, untuk menyelamatkan pengembang lain, kesal karena harus memeriksa mengapa Anda menggunakan bang.

Ledakan ini memberikan dua isyarat kepada pengembang lain.

1) bahwa tidak perlu menyimpan objek setelah memanggil metode.

2) ketika Anda memanggil metode, db akan diubah.

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

Edward Castaño
sumber
6

Penjelasan sederhana:

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.

Tetapi jika Anda pernah memanggil metode downcase!dalam penjelasan di atas, fooakan berubah menjadi downcase secara permanen. downcase!tidak akan mengembalikan objek string baru tetapi mengganti string di tempatnya, benar-benar mengubah fooke huruf kecil. Saya sarankan Anda tidak menggunakan downcase!kecuali itu benar-benar diperlukan.

Fatamorgana
sumber
1
!

Saya suka menganggap ini sebagai perubahan eksplosif yang menghancurkan semua yang telah terjadi sebelumnya. Bang atau tanda seru berarti bahwa Anda membuat perubahan permanen yang disimpan dalam kode Anda.

Jika Anda menggunakan misalnya metode Ruby untuk substitusi global gsub!, substitusi yang Anda buat bersifat permanen.

Cara lain yang dapat Anda bayangkan, adalah membuka file teks dan melakukan pencarian dan ganti, diikuti dengan menyimpan. !melakukan hal yang sama dalam kode Anda.

Pengingat lain yang bermanfaat jika Anda berasal dari dunia bash adalah sed -imemiliki efek yang sama yaitu membuat perubahan permanen yang disimpan.

Charlie Wood
sumber
1

Disebut "Metode Merusak" Mereka cenderung mengubah salinan asli dari objek yang Anda maksud.

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]
Mittinti Ramana Murthy
sumber
0

Intinya: !metode hanya mengubah nilai objek yang mereka panggil, sedangkan metode tanpa !mengembalikan nilai yang dimanipulasi tanpa menulis di atas objek metode dipanggil.

Hanya gunakan !jika Anda tidak berencana membutuhkan nilai asli yang disimpan pada variabel tempat Anda memanggil metode ini.

Saya lebih suka melakukan sesuatu seperti:

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

ATAU

foo = "word"
puts foo.capitalize

Dari pada

foo = "word"
foo.capitalize!
puts foo

Kalau-kalau saya ingin mengakses nilai asli lagi.

Charles
sumber
1
Karena jawaban Anda tidak membantu sama sekali. "Intinya:! Metode hanya mengubah nilai objek yang mereka panggil" tidak benar.
Darwin
@Darwin itu memang mengubah nilai objek. !bermutasi objek daripada mengembalikan salinan yang dimodifikasi.
Charles
Jadi menurut Anda apa yang dilakukannya? User.create!
Darwin
@Darwin dalam konteks apa? ActiveRecord?
Charles
Ya, ActiveRecord.
Darwin