Чому в методах Рубі використовуються знаки оклику?


540

У Ruby в деяких методах є знак питання ( ?), який задає подібне include?запитання, якщо включений відповідний об'єкт, то потім повертається true / false.

Але чому деякі методи мають знаки оклику ( !), де інші не мають?

Що це означає?


21
синонім: вибух, знак оклику
prusswan

17
Прийняту відповідь слід змінити на stackoverflow.com/a/612653/109618 . Дивіться wobblini.net/bang.txt та ruby-forum.com/topic/176830#773946 - "Знак чубчика означає" версія чубчика небезпечніша, ніж її аналог без чуття; поводиться обережно "" -Матц
Девід Дж.

2
Метод удару був би чудовим вибором дизайну, якби тільки і всі методи чубчика були небезпечними. На жаль, їх немає, і тому це стає неприємною вправою запам'ятовування того, що є, а що не можна змінити.
Дейміен Рош

Відповіді:


617

Загалом, методи, які закінчуються, !вказують на те, що метод буде модифікувати об'єкт, на який він викликається . Рубі називає це " небезпечними методами ", оскільки вони змінюють стан, на який може посилатися хтось інший. Ось простий приклад для рядків:

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
Також є такі випадки, як вихід проти виходу! і (в рейках) зберегти проти зберегти!
Ендрю Грімм

24
Будьте дуже обережні - багато менших бібліотек не дотримуються цієї конвенції. Якщо трапляються дивні речі, часто замінюючи obj.wever! з obj = obj.what! виправляє це. Дуже засмучує.
Сара Мей

101
Баг також використовується для методів, які викликають виняток, коли методу без цього немає, наприклад: saveand save!inActiveRecord
ecoologic

3
@AbhilashAK зберегти! викликає помилку, якщо її не вдається зберегти. Це протилежне регулярному збереженню повернення true / false.
BookOfGreg

31
@tgamblin У Рубі існує багато методів, які мутують без чуб. Існують навіть рідкісні методи, які не мутують за допомогою удару, але роблять щось дивовижне, наприклад підвищення помилок або пропуск помилок. Челка використовується для того, щоб сказати, що це більш незвична версія методу, і я думаю, що це має бути відображено у вашій відповіді, оскільки вона позначена як правильна.
BookOfGreg

143

Знак оклику означає багато речей, і іноді ви не можете сказати багато чого, окрім "це небезпечно, будьте обережні".

Як говорили інші, в стандартних методах його часто використовують для позначення методу, який змушує об'єкт мутувати себе, але не завжди. Зверніть увагу , що багато стандартні методи змінюють свій приймач і не мають знак оклику ( pop, shift, clear), а також деякі методи з знаками оклику не змінюють свій приймач ( exit!). Дивіться, наприклад, цю статтю .

Інші бібліотеки можуть використовувати його по-різному. У "Рейлах" знак оклику часто означає, що метод викине виняток на провал, а не на беззвучність.

Це ідентифікація, але багато людей використовують її в тонко різних способах. У вашому власному коді хорошим правилом великих пальців є використання його завжди, коли метод робить щось "небезпечне", особливо коли існують два методи з однаковою назвою і один з них більш "небезпечний", ніж інший. "Небезпечний" може означати майже все, що завгодно.


75

Ця угода про іменування знята зі схеми .

1.3.5 Названня конвенцій

За умовами, назви процедур, які завжди повертають булеве значення, зазвичай закінчуються на "?". Такі процедури називаються предикатами.

За умовами, назви процедур, які зберігають значення у раніше виділених місцях (див. Розділ 3.4), зазвичай закінчуються на ``! ''. Такі процедури називаються мутаційними процедурами. За умовою, значення, повернене процедурою мутації, не визначено.


2
+1 до цієї відповіді, оскільки має документацію, яка дає розумні пояснення для! використання. Дійсно хороша відповідь Стівен
DavidSilveira

Дякую @DavidSilveira!
Стівен Гувіг

24

! зазвичай означає, що метод діє на об'єкт замість повернення результату. З книги програмування Ruby :

Методи, які є "небезпечними" або модифікують приймач, можуть бути названі "".


18

Найточніше сказати, що методи з вибухом! є більш небезпечною чи дивною версією. Існує безліч методів, які мутують без удару, наприклад, .destroyі, як правило, є лише чубки, де в основній зоні існує більш безпечна альтернатива.

Наприклад, у Array у нас є, .compactі .compact!обидва методи мутують масив, але .compact!повертає нуль замість self, якщо в масиві немає нуля, що більш дивно, ніж просто повернення self.

Тільки не мутує метод , який я знайшов з чубчиком це Kernel«s , .exit!який більш дивно , ніж .exitтому , що ви не можете зловити в SystemExitтой час як процес закриття.

Rails та ActiveRecord продовжують цю тенденцію, оскільки вона використовує удар для більш "дивовижних" ефектів, таких як, .create!що призводить до помилок при відмові.


16

Від themomorohoax.com:

Чуб можна використовувати в нижченаведеному порядку, в порядку моїх особистих уподобань.

1) Активний метод запису викликає помилку, якщо метод не робить те, що каже, що буде.

2) Активний метод запису зберігає запис або метод зберігає об'єкт (наприклад, смужка!)

3) Метод робить щось «зайве», як-от повідомлення в іншому місці чи якесь дію.

Справа в тому, що використовуйте чубок лише тоді, коли ви дійсно замислювались над тим, чи потрібно це, щоб врятувати інших розробників від роздратування від необхідності перевіряти, чому ви використовуєте чуб.

Банг надає дві підказки іншим розробникам.

1) що не потрібно зберігати об'єкт після виклику методу.

2) при виклику методу db буде змінено.

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
!

Мені подобається думати про це як про вибухову зміну, яка знищує все, що відбулося до цього. Вибух або знак оклику означає, що ви вносите постійні збережені зміни в свій код.

Якщо ви використовуєте, наприклад, метод Рубі для глобальної заміни, gsub!заміна, яку ви робите, є постійною.

Ще один спосіб уявити це - відкрити текстовий файл і зробити пошук і заміну, а потім зберегти. !робить те саме у вашому коді.

Ще одне корисне нагадування, якщо ви приїхали зі світу баш - 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.
Дарвін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.