Pourquoi les points d'exclamation sont-ils utilisés dans les méthodes Ruby?

540

Dans Ruby, certaines méthodes ont un point d'interrogation ( ?) qui pose une question commeinclude? celle-ci demande si l'objet en question est inclus, cela retourne alors un vrai / faux.

Mais pourquoi certaines méthodes ont-elles des points d'exclamation ( !) alors que d'autres n'en ont pas?

Qu'est-ce que ça veut dire?

Lennie
la source
21
synonyme: bang, point d'exclamation
prusswan
17
La réponse acceptée doit être remplacée par stackoverflow.com/a/612653/109618 . Voir wobblini.net/bang.txt et ruby-forum.com/topic/176830#773946 - "Le signe de coup signifie" que la version coup est plus dangereuse que son homologue non coup; manipuler avec soin "" -Matz
David J.
2
La méthode bang serait un grand choix de conception si seulement et toutes les méthodes bang étaient dangereuses. Malheureusement, ils ne le sont pas, et cela devient donc un exercice frustrant de mémoriser ce qui est et n'est pas mutable.
Damien Roche

Réponses:

617

En général, les méthodes qui se terminent par !indiquent que la méthode modifiera l'objet auquel elle est appelée . Ruby les appelle des " méthodes dangereuses " car elles changent d'état auquel quelqu'un d'autre pourrait avoir une référence. Voici un exemple simple pour les chaînes:

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

Cela produira:

a string

Dans les bibliothèques standard, il y a beaucoup d'endroits où vous verrez des paires de méthodes de nom similaire, une avec !et une sans. Ceux sans sont appelés "méthodes sûres", et ils retournent une copie de l'original avec les modifications appliquées à la copie , avec l'appelé inchangé. Voici le même exemple sans !:

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

Cela produit:

A STRING
a string

Gardez à l'esprit que ce n'est qu'une convention, mais beaucoup de classes Ruby la suivent. Il vous aide également à garder une trace de ce qui est modifié dans votre code.

Todd Gamblin
la source
2
Il y a aussi des cas comme sortie contre sortie! et (dans les rails) enregistrer contre enregistrer!
Andrew Grimm
24
Soyez très prudent - de nombreuses bibliothèques plus petites ne suivent pas cette convention. Si des choses étranges se produisent, en remplaçant souvent obj. avec obj = obj. peu importe! le corrige. Très frustrant.
Sarah Mei
101
bang est également utilisé pour les méthodes qui déclenchent une exception lorsque la méthode sans ne le fait pas, par exemple: saveet save!dansActiveRecord
ecoologic
3
@AbhilashAK save! déclenche une erreur s'il ne peut pas enregistrer. Ceci est opposé à une sauvegarde régulière renvoyant vrai / faux.
BookOfGreg
31
@tgamblin Il existe de nombreuses méthodes dans Ruby qui mute sans frange. Il existe même des méthodes rares qui ne subissent pas de mutation AVEC un bang mais font quelque chose de surprenant comme déclencher des erreurs ou sauter des erreurs. Les franges sont utilisées pour dire que c'est la version la plus inhabituelle de la méthode et je pense que cela devrait se refléter dans votre réponse car elle est marquée comme correcte.
BookOfGreg
143

Le point d'exclamation signifie beaucoup de choses, et parfois vous ne pouvez pas en dire grand-chose d'autre que "c'est dangereux, soyez prudent".

Comme d'autres l'ont dit, dans les méthodes standard, il est souvent utilisé pour indiquer une méthode qui provoque la mutation d'un objet, mais pas toujours. Notez que de nombreuses méthodes standard changer leur récepteur et ne dispose pas d' un point d'exclamation ( pop, shift, clear), et quelques méthodes avec des points d'exclamation ne changent pas leur récepteur ( exit!). Voir cet article par exemple.

D'autres bibliothèques peuvent l'utiliser différemment. Dans Rails, un point d'exclamation signifie souvent que la méthode lèvera une exception en cas d'échec plutôt que d'échouer silencieusement.

C'est une convention d'appellation mais beaucoup de gens l'utilisent de manière subtilement différente. Dans votre propre code, une bonne règle est de l'utiliser chaque fois qu'une méthode fait quelque chose de "dangereux", en particulier lorsque deux méthodes portant le même nom existent et que l'une d'entre elles est plus "dangereuse" que l'autre. "Dangereux" peut cependant signifier presque n'importe quoi.

Brian Carper
la source
75

Cette convention de dénomination est supprimée du schéma .

1.3.5 Conventions de dénomination

Par convention, les noms des procédures qui renvoient toujours une valeur booléenne se terminent généralement par ``? ''. Ces procédures sont appelées prédicats.

Par convention, les noms des procédures qui stockent des valeurs dans des emplacements précédemment alloués (voir section 3.4) se terminent généralement par ``! ''. De telles procédures sont appelées procédures de mutation. Par convention, la valeur renvoyée par une procédure de mutation n'est pas spécifiée.

Steven Huwig
la source
2
+1 à cette réponse car dispose d'une documentation qui donne des explications raisonnables pour le! usage. Vraiment une bonne réponse Steven
DavidSilveira
Merci @DavidSilveira!
Steven Huwig
24

! signifie généralement que la méthode agit sur l'objet au lieu de renvoyer un résultat. Extrait du livre Programming Ruby :

Les méthodes qui sont «dangereuses» ou qui modifient le récepteur peuvent être nommées avec un «!» De fin.

Pesto
la source
18

Il est plus juste de dire que les méthodes avec un Bang! sont la version la plus dangereuse ou surprenante . Il existe de nombreuses méthodes qui mutent sans Bang, telles que.destroy et en général, les méthodes n'ont que des bangs où une alternative plus sûre existe dans la lib de base.

Par exemple, sur Array, nous avons .compactet .compact!, les deux méthodes mutent le tableau, mais.compact! retournent nil au lieu de self s'il n'y a pas de nil dans le tableau, ce qui est plus surprenant que de simplement retourner self.

La seule méthode non que j'ai trouvé muter avec un bang Kernel« s .exit!qui est plus surprenant que .exitparce que vous ne pouvez pas attraper SystemExitalors que le processus se ferme.

Rails et ActiveRecord poursuivent cette tendance dans la mesure où ils utilisent le bang pour des effets plus «surprenants» comme ceux .create!qui provoquent des erreurs en cas d'échec.

BookOfGreg
la source
16

De themomorohoax.com:

Un bang peut être utilisé de la manière suivante, dans l'ordre de mes préférences personnelles.

1) Une méthode d'enregistrement active génère une erreur si la méthode ne fait pas ce qu'elle dit.

2) Une méthode d'enregistrement active enregistre l'enregistrement ou une méthode enregistre un objet (par exemple une bande!)

3) Une méthode fait quelque chose de «supplémentaire», comme des publications à un endroit ou fait une action.

Le point est: n'utilisez un bang que lorsque vous avez vraiment réfléchi à la nécessité, pour éviter aux autres développeurs la gêne d'avoir à vérifier pourquoi vous utilisez un bang.

Le bang fournit deux indices aux autres développeurs.

1) qu'il n'est pas nécessaire de sauvegarder l'objet après avoir appelé la méthode.

2) lorsque vous appelez la méthode, la base de données va être modifiée.

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

Edward Castaño
la source
6

Explication simple:

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.

Mais si jamais vous appelez une méthode downcase!dans l'explication ci-dessus, fooelle deviendrait définitivement downcase. downcase!ne retournerait pas un nouvel objet chaîne mais remplacerait la chaîne en place, changeant totalement le fooen downcase. Je vous suggère de ne pas l'utiliser à downcase!moins que ce ne soit totalement nécessaire.

Mirage
la source
1
!

J'aime à voir cela comme un changement explosif qui détruit tout ce qui l'a précédé. Un coup ou un point d'exclamation signifie que vous apportez une modification enregistrée permanente dans votre code.

Si vous utilisez par exemple la méthode de Ruby pour la substitution globalegsub! la substitution que vous effectuez est permanente.

Vous pouvez également l'imaginer en ouvrant un fichier texte et en effectuant une recherche et un remplacement, suivi de l'enregistrement. !fait de même dans votre code.

Un autre rappel utile si vous venez du monde bash est sed -ia cet effet similaire de faire un changement enregistré permanent.

Charlie Wood
la source
1

Appelées «méthodes destructives», elles ont tendance à modifier la copie originale de l'objet auquel vous faites référence.

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
la source
0

Conclusion: les !méthodes modifient simplement la valeur de l'objet auquel elles sont appelées, tandis qu'une méthode sans! retourne une valeur manipulée sans écraser l'objet auquel la méthode a été appelée.

À utiliser uniquement !si vous ne prévoyez pas d'avoir besoin de la valeur d'origine stockée dans la variable sur laquelle vous avez appelé la méthode.

Je préfère faire quelque chose comme:

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

OU

foo = "word"
puts foo.capitalize

Au lieu de

foo = "word"
foo.capitalize!
puts foo

Juste au cas où je voudrais accéder à nouveau à la valeur d'origine.

Charles
la source
1
Parce que votre réponse n'a été d'aucune façon utile. "Conclusion:! Les méthodes changent simplement la valeur de l'objet auquel elles sont appelées" n'est tout simplement pas vrai.
Darwin
@Darwin il fait modifier la valeur de l'objet. !mute l'objet plutôt que de renvoyer une copie modifiée.
Charles
Alors, que pensez-vous que cela fait? User.create!
Darwin
@Darwin dans quel contexte? ActiveRecord?
Charles
Oui, ActiveRecord.
Darwin