Warum werden in Ruby-Methoden Ausrufezeichen verwendet?

540

In Ruby haben einige Methoden ein Fragezeichen ( ?), das eine solche include?Frage stellt. Wenn das betreffende Objekt enthalten ist, wird ein true / false zurückgegeben.

Aber warum haben einige Methoden Ausrufezeichen ( !), andere nicht?

Was bedeutet das?

Lennie
quelle
21
Synonym: Knall, Ausrufezeichen
Prusswan
17
Die akzeptierte Antwort sollte in stackoverflow.com/a/612653/109618 geändert werden . Siehe Wobblini.net/bang.txt und ruby-forum.com/topic/176830#773946 - "Das Knallzeichen bedeutet " Die Knallversion ist gefährlicher als ihr Gegenstück ohne Knall. vorsichtig behandeln "" -Matz
David J.
2
Die Knallmethode wäre eine gute Wahl für das Design, wenn nur und alle Knallmethoden gefährlich wären . Leider sind sie es nicht und so wird es zu einer frustrierenden Übung, sich zu merken, was veränderlich ist und was nicht.
Damien Roche

Antworten:

617

Im Allgemeinen geben Methoden, die mit enden, an !, dass die Methode das Objekt ändert, für das sie aufgerufen wird . Ruby nennt diese " gefährliche Methoden ", weil sie den Status ändern, auf den möglicherweise jemand anderes verweist. Hier ist ein einfaches Beispiel für Zeichenfolgen:

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

Dies wird Folgendes ausgeben:

a string

In den Standardbibliotheken gibt es viele Stellen, an denen Paare von Methoden mit ähnlichen Namen angezeigt werden, eine mit !und eine ohne. Diejenigen ohne werden als "sichere Methoden" bezeichnet und geben eine Kopie des Originals mit Änderungen an der Kopie zurück , wobei der Angerufene unverändert bleibt. Hier ist das gleiche Beispiel ohne !:

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

Dies gibt aus:

A STRING
a string

Denken Sie daran, dass dies nur eine Konvention ist, aber viele Ruby-Klassen folgen ihr. Es hilft Ihnen auch zu verfolgen, was in Ihrem Code geändert wird.

Todd Gamblin
quelle
2
Es gibt auch Fälle wie Exit versus Exit! und (in Schienen) sparen gegen sparen!
Andrew Grimm
24
Seien Sie sehr vorsichtig - viele kleinere Bibliotheken folgen dieser Konvention nicht. Wenn seltsame Dinge passieren, ersetzen Sie oft obj.was auch immer! mit obj = obj.was auch immer! behebt es. Sehr frustrierend.
Sarah Mei
101
Bang wird auch für Methoden verwendet, die eine Ausnahme savesave!ActiveRecord
auslösen,
3
@AbhilashAK speichern! löst einen Fehler aus, wenn er nicht gespeichert werden kann. Dies steht im Gegensatz zum regulären Speichern, das true / false zurückgibt.
BookOfGreg
31
@tgamblin Es gibt viele Methoden in Ruby, die ohne Pony mutieren. Es gibt sogar seltene Methoden, die nicht MIT einem Knall mutieren, sondern etwas Überraschendes tun, wie Fehler auslösen oder Fehler überspringen. Pony werden verwendet, um zu sagen, dass dies die ungewöhnlichere Version der Methode ist, und ich denke, dies sollte sich in Ihrer Antwort widerspiegeln, da sie als korrekt markiert ist.
BookOfGreg
143

Das Ausrufezeichen bedeutet viele Dinge, und manchmal kann man nicht viel davon sagen, außer "das ist gefährlich, sei vorsichtig".

Wie andere gesagt haben, wird es in Standardmethoden häufig verwendet, um eine Methode anzugeben, die bewirkt, dass sich ein Objekt selbst mutiert, jedoch nicht immer. Beachten Sie, dass viele Standard - Methoden , um ihren Empfänger ändern und stellen nicht ein Ausrufezeichen haben ( pop, shift, clear) und einige Methoden mit Ausrufungszeichen nicht ihren Empfänger nicht ändern ( exit!). Siehe diesen Artikel zum Beispiel.

Andere Bibliotheken verwenden es möglicherweise anders. In Rails bedeutet ein Ausrufezeichen häufig, dass die Methode bei einem Fehler eine Ausnahme auslöst, anstatt stillschweigend fehlzuschlagen.

Es ist eine Namenskonvention, aber viele Leute verwenden sie auf subtil unterschiedliche Weise. In Ihrem eigenen Code ist es eine gute Faustregel, ihn immer dann zu verwenden, wenn eine Methode etwas "Gefährliches" tut, insbesondere wenn zwei Methoden mit demselben Namen existieren und eine davon "gefährlicher" als die andere ist. "Gefährlich" kann jedoch fast alles bedeuten.

Brian Carper
quelle
75

Diese Namenskonvention wird aus dem Schema gestrichen .

1.3.5 Namenskonventionen

Konventionell enden die Namen von Prozeduren, die immer einen booleschen Wert zurückgeben, normalerweise mit "?". Solche Prozeduren werden Prädikate genannt.

Konventionell enden die Namen von Prozeduren, die Werte an zuvor zugewiesenen Speicherorten speichern (siehe Abschnitt 3.4), normalerweise mit "!". Solche Verfahren werden Mutationsverfahren genannt. Konventionell ist der von einem Mutationsverfahren zurückgegebene Wert nicht angegeben.

Steven Huwig
quelle
2
+1 auf diese Antwort hat da eine Dokumentation, die vernünftige Erklärungen für die! Verwendungszweck. Wirklich gute Antwort Steven
DavidSilveira
Danke @DavidSilveira!
Steven Huwig
24

! In der Regel bedeutet dies, dass die Methode auf das Objekt einwirkt, anstatt ein Ergebnis zurückzugeben. Aus dem Buch Programming Ruby :

Methoden, die "gefährlich" sind oder den Empfänger modifizieren, werden möglicherweise mit einem nachgestellten "!" Benannt.

Pesto
quelle
18

Es ist am genauesten zu sagen, dass Methoden mit einem Knall! sind die gefährlichere oder überraschendere Version. Es gibt viele Methoden, die ohne einen Knall mutieren, wie .destroyund im Allgemeinen haben Methoden nur Pony, wo eine sicherere Alternative in der Kernbibliothek existiert.

Zum Beispiel haben wir auf Array .compactund .compact!, beide Methoden mutieren das Array, geben aber .compact!nil anstelle von self zurück, wenn das Array keine nil enthält, was überraschender ist, als nur self zurückzugeben.

Die einzige nicht mutierende Methode, die ich mit einem Knall gefunden habe, ist die Kernel, .exit!die überraschender ist, als .exitweil Sie nicht fangen können, SystemExitwährend der Prozess geschlossen wird.

Rails und ActiveRecord setzen diesen Trend fort, indem sie Bang für "überraschendere" Effekte verwenden .create!, die Fehler bei Fehlern verursachen.

BookOfGreg
quelle
16

Von themomorohoax.com:

Ein Knall kann in der Reihenfolge meiner persönlichen Vorlieben auf die folgenden Arten verwendet werden.

1) Eine aktive Aufzeichnungsmethode löst einen Fehler aus, wenn die Methode nicht das tut, was sie verspricht.

2) Eine aktive Aufzeichnungsmethode speichert den Datensatz oder eine Methode speichert ein Objekt (z. B. Streifen!)

3) Eine Methode macht etwas "Extra", wie Beiträge an einen Ort, oder führt eine Aktion aus.

Der Punkt ist: Verwenden Sie einen Knall nur, wenn Sie wirklich darüber nachgedacht haben, ob dies notwendig ist, um anderen Entwicklern den Ärger zu ersparen, überprüfen zu müssen, warum Sie einen Knall verwenden.

Der Knall bietet anderen Entwicklern zwei Hinweise.

1) dass es nicht notwendig ist, das Objekt nach dem Aufruf der Methode zu speichern.

2) Wenn Sie die Methode aufrufen, wird die Datenbank geändert.

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

Edward Castaño
quelle
6

Einfache Erklärung:

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.

Wenn Sie jedoch jemals eine Methode downcase!in der obigen Erklärung aufgerufen haben , foowürde sich diese dauerhaft in einen Kleinbuchstaben ändern. downcase!würde kein neues Zeichenfolgenobjekt zurückgeben, sondern die vorhandene Zeichenfolge ersetzen und das fooin Großbuchstaben ändern. Ich schlage vor, Sie verwenden downcase!nicht, es sei denn, es ist absolut notwendig.

Fata Morgana
quelle
1
!

Ich stelle mir das gerne als eine explosive Veränderung vor, die alles zerstört, was vorher gegangen ist. Ein Knall oder ein Ausrufezeichen bedeutet, dass Sie eine dauerhaft gespeicherte Änderung in Ihrem Code vornehmen.

Wenn Sie beispielsweise Rubys Methode für die globale Substitution verwenden, ist gsub!die von Ihnen vorgenommene Substitution dauerhaft.

Eine andere Möglichkeit, die Sie sich vorstellen können, besteht darin, eine Textdatei zu öffnen, zu suchen und zu ersetzen und anschließend zu speichern. !macht das gleiche in Ihrem Code.

Eine weitere nützliche Erinnerung, wenn Sie aus der Bash-Welt kommen, ist sed -ider ähnliche Effekt, dass Sie dauerhaft gespeicherte Änderungen vornehmen.

Charlie Wood
quelle
1

Als "destruktive Methoden" bezeichnet Sie ändern in der Regel die Originalkopie des Objekts, auf das Sie sich beziehen.

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

Fazit: !Methoden ändern nur den Wert des Objekts, für das sie aufgerufen werden, während eine Methode ohne !einen manipulierten Wert zurückgibt, ohne über das Objekt zu schreiben, für das die Methode aufgerufen wurde.

Nur verwenden, !wenn Sie nicht vorhaben, den ursprünglichen Wert zu benötigen, der in der Variablen gespeichert ist, für die Sie die Methode aufgerufen haben.

Ich mache lieber so etwas wie:

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

ODER

foo = "word"
puts foo.capitalize

Anstatt

foo = "word"
foo.capitalize!
puts foo

Nur für den Fall, dass ich wieder auf den ursprünglichen Wert zugreifen möchte.

Charles
quelle
1
Weil Ihre Antwort in keiner Weise hilfreich war. "Fazit :! Methoden ändern nur den Wert des Objekts, das sie aufgerufen werden" ist einfach nicht wahr.
Darwin
@Darwin es tut den Wert des Objekts ändern. !mutiert das Objekt, anstatt eine geänderte Kopie zurückzugeben.
Charles
Was glaubst du, macht das? User.create!
Darwin
@ Darwin in welchem ​​Kontext? Aktiver Rekord?
Charles
Ja, ActiveRecord.
Darwin