Por que os pontos de exclamação são usados ​​nos métodos Ruby?

540

Em Ruby, alguns métodos têm um ponto de interrogação ( ?) que faz uma pergunta como include?essa e pergunta se o objeto em questão está incluído; isso retorna um verdadeiro / falso.

Mas por que alguns métodos têm pontos de exclamação ( !), enquanto outros não?

O que isso significa?

Lennie
fonte
21
sinônimo: bang, ponto de exclamação
prusswan
17
A resposta aceita deve ser alterada para stackoverflow.com/a/612653/109618 . Veja wobblini.net/bang.txt e ruby-forum.com/topic/176830#773946 - "O sinal de estrondo significa" que a versão do estrondo é mais perigosa do que sua contraparte; manuseie com cuidado "" -Matz
David J.
2
O método bang seria uma ótima opção de design se apenas e todos os métodos bang fossem perigosos. Infelizmente eles não são, e por isso se torna um exercício frustrante de memorizar o que é e o que não é mutável.
Damien Roche

Respostas:

617

Em geral, os métodos que terminam !indicam que o método modificará o objeto em que é chamado . Ruby os chama de " métodos perigosos " porque eles mudam de estado ao qual outra pessoa pode ter uma referência. Aqui está um exemplo simples para strings:

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

Isso produzirá:

a string

Nas bibliotecas padrão, há muitos lugares em que você verá pares de métodos com nomes semelhantes, um com !e outro sem. Os que estão sem são chamados de "métodos seguros" e retornam uma cópia do original com as alterações aplicadas à cópia , com o chamado inalterado. Aqui está o mesmo exemplo sem o !:

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

Isso gera:

A STRING
a string

Lembre-se de que isso é apenas uma convenção, mas muitas classes de Ruby a seguem. Também ajuda a acompanhar o que está sendo modificado no seu código.

Todd Gamblin
fonte
2
Também existem casos como saída versus saída! e (nos trilhos) salvar versus salvar!
Andrew Grimm
24
Tenha muito cuidado - muitas bibliotecas menores não seguem esta convenção. Se coisas estranhas estão acontecendo, geralmente substituindo obj.whatever! com obj = obj.whatever! corrige isso. Muito frustrante.
246 Sarah Sarah Mei
101
golpe, também é utilizada para métodos que aumentam uma excepção, quando o método sem não, por exemplo,: savee save!naActiveRecord
ecoologic
3
@AbhilashAK save! gera um erro se não puder salvar. Isso se opõe ao salvamento regular, retornando verdadeiro / falso.
BookOfGreg
31
@tgamblin Existem muitos métodos no Ruby que sofrem mutações sem franja. Existem até métodos raros que não mudam COM um estrondo, mas fazem algo surpreendente, como aumentar erros ou ignorar erros. Bangs costumam dizer que esta é a versão mais incomum do método e acho que isso deve se refletir em sua resposta, pois está marcado como correto.
BookOfGreg
143

O ponto de exclamação significa muitas coisas e, às vezes, você não pode dizer muita coisa além de "isso é perigoso, tenha cuidado".

Como outros já disseram, em métodos padrão, é freqüentemente usado para indicar um método que faz com que um objeto se mude, mas nem sempre. Note-se que muitos métodos padrão mudar seu receptor e não tem um ponto de exclamação ( pop, shift, clear), e alguns métodos com pontos de exclamação não mudam seu receptor ( exit!). Veja este artigo por exemplo.

Outras bibliotecas podem usá-lo de maneira diferente. No Rails, um ponto de exclamação geralmente significa que o método lançará uma exceção em caso de falha, em vez de falhar silenciosamente.

É uma convenção de nomes, mas muitas pessoas a usam de maneiras sutilmente diferentes. No seu próprio código, uma boa regra de ouro é usá-lo sempre que um método está fazendo algo "perigoso", especialmente quando existem dois métodos com o mesmo nome e um deles é mais "perigoso" que o outro. "Perigoso" pode significar quase qualquer coisa.

Brian Carper
fonte
75

Esta convenção de nomenclatura é retirada do esquema .

1.3.5 Convenções de nomenclatura

Por convenção, os nomes dos procedimentos que sempre retornam um valor booleano geralmente terminam em ``? ''. Tais procedimentos são chamados de predicados.

Por convenção, os nomes dos procedimentos que armazenam valores em locais previamente alocados (consulte a seção 3.4) geralmente terminam em ``! ''. Tais procedimentos são chamados de procedimentos de mutação. Por convenção, o valor retornado por um procedimento de mutação não é especificado.

Steven Huwig
fonte
2
+1 a esta resposta, pois possui uma documentação que fornece explicações razoáveis ​​para o! uso. Realmente boa resposta Steven
DavidSilveira
Obrigado @DavidSilveira!
Steven Huwig
24

! normalmente significa que o método atua sobre o objeto em vez de retornar um resultado. Do livro Programming Ruby :

Métodos que são "perigosos" ou modificam o receptor podem ser nomeados com um "!" À direita.

Pesto
fonte
18

É mais preciso dizer que os métodos com um Bang! são a versão mais perigosa ou surpreendente . Existem muitos métodos que mudam sem um Bang, como .destroyem geral os métodos têm apenas bangs onde existe uma alternativa mais segura na biblioteca principal.

Por exemplo, em Array, temos .compacte .compact!, ambos os métodos modificam a matriz, mas .compact!retornam nulo em vez de self se não houver nulo na matriz, o que é mais surpreendente do que apenas retornar self.

O único método não-mutante que encontrei com um estrondo é Kernelo .exit!mais surpreendente do que .exitporque você não pode pegar SystemExitenquanto o processo está fechando.

O Rails e o ActiveRecord continuam essa tendência, pois usam o bang para obter efeitos mais "surpreendentes", como o .create!que gera erros em caso de falha.

BookOfGreg
fonte
16

Em themomorohoax.com:

Um estrondo pode ser usado das maneiras abaixo, em ordem de minha preferência pessoal.

1) Um método de registro ativo gera um erro se o método não fizer o que diz que fará.

2) Um método de registro ativo salva o registro ou um método salva um objeto (por exemplo, tira!)

3) Um método faz algo "extra", como postagens em algum lugar ou faz alguma ação.

O ponto é: use apenas um estrondo quando você realmente tiver pensado se é necessário, para salvar outros desenvolvedores do incômodo de verificar por que você está usando um estrondo.

O estrondo fornece duas pistas para outros desenvolvedores.

1) que não é necessário salvar o objeto depois de chamar o método.

2) quando você chama o método, o banco de dados será alterado.

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

Edward Castaño
fonte
6

Explicação simples:

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.

Mas se você já chamou um método downcase!na explicação acima, foomudaria para minúscula permanentemente. downcase!não retornaria um novo objeto de sequência, mas substituiria a sequência no lugar, alterando totalmente foopara minúscula. Eu sugiro que você não use a downcase!menos que seja totalmente necessário.

Miragem
fonte
1
!

Eu gosto de pensar nisso como uma mudança explosiva que destrói tudo o que passou antes dela. Bang ou ponto de exclamação significa que você está fazendo uma alteração salva permanente no seu código.

Se você usar, por exemplo, o método Ruby para substituição global, gsub!a substituição feita será permanente.

Outra maneira que você pode imaginar é abrir um arquivo de texto e localizar e substituir, seguido pelo salvamento. !faz o mesmo no seu código.

Outro lembrete útil, se você vem do mundo do bash, sed -item esse efeito semelhante de fazer alterações salvas permanentes.

Charlie Wood
fonte
1

Chamados de "Métodos destrutivos" Eles tendem a alterar a cópia original do objeto ao qual você está se referindo.

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
fonte
0

Conclusão: os !métodos apenas alteram o valor do objeto em que são chamados, enquanto um método sem !retorna um valor manipulado sem escrever sobre o objeto em que o método foi chamado.

Use apenas !se você não planeja precisar o valor original armazenado na variável na qual você chamou o método.

Eu prefiro fazer algo como:

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

OU

foo = "word"
puts foo.capitalize

Ao invés de

foo = "word"
foo.capitalize!
puts foo

Apenas no caso de eu gostaria de acessar o valor original novamente.

Charles
fonte
1
Porque sua resposta não foi útil de forma alguma. "Bottom line:! Métodos apenas alteram o valor do objeto que eles são chamados" simplesmente não é verdade.
Darwin
@Darwin que faz alterar o valor do objeto. !modifica o objeto em vez de retornar uma cópia modificada.
Charles
Então, o que você acha que isso faz? User.create!
Darwin
@ Darwin em que contexto? ActiveRecord?
Charles
Sim, ActiveRecord.
Darwin