Почему восклицательные знаки используются в методах 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!которые вызывают ошибки при сбое.


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.
Дарвин
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.