Почему восклицательные знаки используются в методах Ruby?

540

В Ruby некоторые методы имеют вопросительный знак ( ?), который задает вопрос, подобный тому, include?который задается , если включается рассматриваемый объект, а затем возвращает true / false.

Но почему некоторые методы имеют восклицательные знаки ( !), а другие нет?

Что это значит?

Ленни
источник
21
синоним: взрыва, восклицательный знак
пруссван
17
Принятый ответ должен быть изменен на stackoverflow.com/a/612653/109618 . См. Wobblini.net/bang.txt и ruby-forum.com/topic/176830#773946 - «Знак взрыва означает», что версия взрыва более опасна, чем ее коллега без взрыва; обращаться с осторожностью "" -Матц
Дэвид Дж
2
Метод взрыва был бы отличным выбором дизайна, если бы только и все методы взрыва были опасны. К сожалению, это не так, и поэтому это становится разочаровывающим упражнением в запоминании того, что есть, а что нет.
Дэмиен Рош

Ответы:

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
Есть также случаи, как выход против выхода! и (в рельсах) сохранить против сохранить!
Эндрю Гримм
24
Будьте очень осторожны - многие небольшие библиотеки не следуют этому соглашению. Если происходят странные вещи, часто заменяют obj.wh чем угодно! с obj = obj. что угодно! исправляет это. Очень расстраивает.
Сара Мей
101
Взрыв также используется для методов, которые вызывают исключение, когда метод без него, например: saveи save!вActiveRecord
ecoologic
3
@AbhilashAK сохранить! выдает ошибку, если не может сохранить. Это противоположно обычному сохранению, возвращающему истину / ложь.
BookOfGreg
31
@tgamblin В Ruby существует множество методов, которые мутируют без ударов. Есть даже редкие методы, которые не изменяют С ударом, но делают что-то удивительное, например, ошибки повышения или пропуска ошибок. Удары используются, чтобы сказать, что это более необычная версия метода, и я думаю, что это должно быть отражено в вашем ответе, поскольку он помечен как правильный.
BookOfGreg
143

Восклицательный знак означает много вещей, и иногда вы не можете многое сказать из этого, кроме «это опасно, будьте осторожны».

Как уже говорили другие, в стандартных методах он часто используется для обозначения метода, который заставляет объект мутировать сам, но не всегда. Обратите внимание , что многие стандартные методы меняют свой приемник и не имеют восклицательный знак ( pop, shift, clear), а также некоторые методы с восклицательными знаками не меняют свой приемник ( exit!). Смотрите эту статью для примера.

Другие библиотеки могут использовать его по-другому. В Rails восклицательный знак часто означает, что метод будет генерировать исключение при сбое, а не молча.

Это соглашение об именах, но многие люди используют его немного по-разному. В вашем собственном коде хорошее правило - использовать его всякий раз, когда метод делает что-то «опасное», особенно когда существуют два метода с одинаковым именем, и один из них более «опасен», чем другой. «Опасный» может означать почти все что угодно.

Брайан Карпер
источник
75

Это соглашение об именах взято из Схемы .

1.3.5 Соглашения об именах

По соглашению имена процедур, которые всегда возвращают логическое значение, обычно заканчиваются на `?? '. Такие процедуры называются предикатами.

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

Стивен Хьюиг
источник
2
+1 к этому ответу, так как имеет документацию, которая дает разумные объяснения для! Применение. Действительно хороший ответ Стивен
DavidSilveira
Спасибо @DavidSilveira!
Стивен Хьюиг,
24

! обычно означает, что метод воздействует на объект, а не возвращает результат. Из книги « Программирование на Ruby» :

Методы, которые «опасны» или модифицируют получателя, могут быть названы завершающим «!».

Песто
источник
18

Наиболее точно можно сказать, что методы на ура! являются более опасной или удивительной версией. Есть много методов, которые мутируют без взрыва, таких как .destroyи вообще методы имеют взрыва только там, где в базовой библиотеке существует более безопасная альтернатива.

Например, на массиве у нас есть .compactи .compact!оба метода мутировать массив, но .compact!возвращает NIL вместо себя , если нет нильполугруппы находится в массиве, что более удивительно , чем просто возвращение себя.

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

Rails и ActiveRecord продолжают эту тенденцию в том смысле, что они используют взрыв для более «удивительных» эффектов, например, .create!которые вызывают ошибки при сбое.

BookOfGreg
источник
16

С themomorohoax.com:

Взрыв может использоваться следующими способами, в порядке моих личных предпочтений.

1) Метод активной записи вызывает ошибку, если метод не делает то, что говорит.

2) Метод активной записи сохраняет запись или метод сохраняет объект (например, strip!)

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. 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.
Дарвин