Dlaczego wykrzykniki są używane w metodach Ruby?

540

W języku Ruby niektóre metody mają znak zapytania ( ?), który zadaje takie pytanie include?, czy pytany obiekt jest uwzględniony, a następnie zwraca wartość prawda / fałsz.

Ale dlaczego niektóre metody mają wykrzykniki ( !), a inne nie?

Co to znaczy?

Lennie
źródło
21
synonim: huk, wykrzyknik
prusswan
17
Zaakceptowana odpowiedź powinna zostać zmieniona na stackoverflow.com/a/612653/109618 . Zobacz wobblini.net/bang.txt i ruby-forum.com/topic/176830#773946 - „Znak huku oznacza, że” wersja huku jest bardziej niebezpieczna niż jej odpowiednik inny niż huk; obchodź się ostrożnie „” -Matz
David J.
2
Metoda hukowa byłaby doskonałym wyborem, gdyby tylko i wszystkie metody huku były niebezpieczne. Niestety nie są, więc staje się frustrującym ćwiczeniem w zapamiętywaniu tego, co jest, a czego nie można modyfikować.
Damien Roche,

Odpowiedzi:

617

Zasadniczo metody kończące się na !wskazują, że metoda zmodyfikuje obiekt, do którego została wywołana . Ruby nazywa je „ niebezpiecznymi metodami ”, ponieważ zmieniają stan, do którego może odwoływać się ktoś inny. Oto prosty przykład dla ciągów:

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

Spowoduje to:

a string

W bibliotekach standardowych jest wiele miejsc, w których zobaczysz pary podobnie nazwanych metod, jedną z tą, !a drugą bez. Te bez nazywane są „bezpiecznymi metodami” i zwracają kopię oryginału ze zmianami zastosowanymi do kopii , bez zmiany strony. Oto ten sam przykład bez !:

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

To daje:

A STRING
a string

Pamiętaj, że to tylko konwencja, ale przestrzega ją wiele klas Ruby. Pomaga także śledzić zmiany w kodzie.

Todd Gamblin
źródło
2
Są też przypadki takie jak wyjście kontra wyjście! i (w szynach) zapisz kontra zapisz!
Andrew Grimm,
24
Bądź bardzo ostrożny - wiele mniejszych bibliotek nie przestrzega tej konwencji. Jeśli dzieją się dziwne rzeczy, często zastępując obj. Cokolwiek! z obj = obj. niezależnie od tego! naprawia to. Bardzo frustrujące.
Sarah Mei,
101
huk jest również stosowany w przypadku metod, które savesave!ActiveRecord
zgłaszają
3
@AbhilashAK oszczędzaj! zgłasza błąd, jeśli nie można zapisać. Jest to przeciwieństwo zwykłego zapisywania zwracającego wartość prawda / fałsz.
BookOfGreg
31
@tgamblin Istnieje wiele metod w Ruby, które mutują bez grzywki. Istnieją nawet rzadkie metody, które nie mutują Z WYBUCHEM, ale robią coś zaskakującego, na przykład błędy podnoszenia lub pomijania błędów. Grzywka mówi się, że jest to bardziej niezwykła wersja metody i myślę, że powinno to znaleźć odzwierciedlenie w twojej odpowiedzi, ponieważ jest ona oznaczona jako poprawna.
BookOfGreg
143

Wykrzyknik oznacza wiele rzeczy, a czasem nie można wiele z tego powiedzieć inaczej niż „to niebezpieczne, bądź ostrożny”.

Jak powiedzieli inni, w standardowych metodach jest często używany do wskazania metody, która powoduje, że obiekt sam się mutuje, ale nie zawsze. Należy pamiętać, że wiele standardowych metod zmienić swój odbiornik i nie mają wykrzyknik ( pop, shift, clear), a niektóre metody z wykrzykników nie zmieniają odbiornik ( exit!). Zobacz na przykład ten artykuł .

Inne biblioteki mogą używać go inaczej. W Railsach wykrzyknik często oznacza, że ​​metoda zgłosi wyjątek w przypadku niepowodzenia, a nie po cichu.

Jest to konwencja nazewnictwa, ale wiele osób używa jej w subtelny sposób. W twoim własnym kodzie dobrą zasadą jest używanie go za każdym razem, gdy metoda robi coś „niebezpiecznego”, szczególnie gdy istnieją dwie metody o tej samej nazwie, a jedna z nich jest bardziej „niebezpieczna” niż druga. „Niebezpieczny” może jednak znaczyć prawie wszystko.

Brian Carper
źródło
75

Ta konwencja nazewnictwa została usunięta ze schematu .

1.3.5 Konwencje nazewnictwa

Zgodnie z konwencją nazwy procedur, które zawsze zwracają wartość logiczną, zwykle kończą się na ``? ''. Takie procedury nazywane są predykatami.

Zgodnie z konwencją nazwy procedur przechowujących wartości w uprzednio przydzielonych lokalizacjach (patrz sekcja 3.4) zwykle kończą się na ``! ''. Takie procedury nazywane są procedurami mutacji. Zgodnie z konwencją wartość zwracana przez procedurę mutacji nie jest określona.

Steven Huwig
źródło
2
+1 do tej odpowiedzi, ponieważ ma dokumentację, która daje uzasadnione wyjaśnienia dla! stosowanie. Naprawdę dobra odpowiedź Steven
DavidSilveira,
Dzięki @DavidSilveira!
Steven Huwig
24

! zazwyczaj oznacza, że ​​metoda działa na obiekt, zamiast zwracać wynik. Z książki Programowanie Ruby :

Metody, które są „niebezpieczne” lub modyfikują odbiornik, można nazwać końcowym „!”.

pesto
źródło
18

Najdokładniej jest powiedzieć, że metody z hukiem! są bardziej niebezpieczną lub zaskakującą wersją. Istnieje wiele metod mutacji bez huku, takich jak .destroyi ogólnie metody mają tylko huk, w którym istnieje bezpieczniejsza alternatywa w podstawowej wersji biblioteki.

Na przykład w Array mamy .compacti .compact!obie metody mutują tablicę, ale .compact!zwraca zero zamiast self, jeśli nie ma żadnych zer w tablicy, co jest bardziej zaskakujące niż zwykłe zwracanie self.

Jedyna metoda bez mutacji ja z hukiem znaleźć to Kernel„s .exit!, która jest bardziej zaskakujące, niż .exitdlatego, że nie może złapać SystemExitpodczas procesu zamykania.

Railsy i ActiveRecord kontynuują ten trend, wykorzystując huk dla bardziej „zaskakujących” efektów, takich jak .create!wywoływanie błędów po awarii.

BookOfGreg
źródło
16

Od themomorohoax.com:

Wybuchu można używać w następujący sposób, w zależności od moich osobistych preferencji.

1) Aktywna metoda zapisu wywołuje błąd, jeśli metoda nie robi tego, co jej się podoba.

2) Aktywna metoda zapisu zapisuje zapis lub metoda zapisuje obiekt (np. Pasek!)

3) Metoda robi coś „dodatkowego”, na przykład posty do jakiegoś miejsca, lub wykonuje pewne działania.

Chodzi o to: używaj huku tylko wtedy, gdy naprawdę zastanawiasz się, czy jest to konieczne, aby zaoszczędzić innym programistom irytujące konieczność sprawdzania, dlaczego używasz huku.

Bang zapewnia dwa sygnały innym programistom.

1) nie jest konieczne zapisywanie obiektu po wywołaniu metody.

2) po wywołaniu metody db zostanie zmieniony.

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

Edward Castaño
źródło
6

Proste wyjaśnienie:

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.

Ale gdybyś kiedykolwiek nazwał metodę downcase!w powyższym wyjaśnieniu, foozmieniłby się na stałe na małe. downcase!nie zwróci nowego obiektu ciągu, ale zastąpi go w miejscu, całkowicie zmieniając na foomałe. Radzę nie używać, downcase!chyba że jest to absolutnie konieczne.

Miraż
źródło
1
!

Lubię myśleć o tym jak o wybuchowej zmianie, która niszczy wszystko, co przed nią minęło. Uderzenie lub wykrzyknik oznacza, że ​​dokonujesz trwałej zapisanej zmiany w kodzie.

Jeśli użyjesz na przykład metody Ruby do globalnej substytucji, gsub!dokonana przez ciebie podstawa jest trwała.

Innym sposobem, w jaki możesz to sobie wyobrazić, jest otwarcie pliku tekstowego, wyszukiwanie i zamiana, a następnie zapisywanie. !robi to samo w kodzie.

Kolejnym przydatnym przypomnieniem, jeśli pochodzisz ze świata bash, jest sed -ipodobny efekt wprowadzenia trwale zapisanych zmian.

Charlie Wood
źródło
1

Nazywane „metodami niszczącymi” Mają tendencję do zmiany oryginalnej kopii obiektu, do którego się odnosisz.

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
źródło
0

Konkluzja: !metody zmieniają tylko wartość obiektu, do którego są wywoływane, podczas gdy metoda bez !zwraca wartość zmanipulowaną bez zapisywania obiektu, do którego została wywołana metoda.

Używaj tylko !wtedy, gdy nie planujesz potrzebować oryginalnej wartości przechowywanej w zmiennej, na której wywołałeś metodę.

Wolę robić coś takiego:

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

LUB

foo = "word"
puts foo.capitalize

Zamiast

foo = "word"
foo.capitalize!
puts foo

Na wszelki wypadek chciałbym uzyskać dostęp do oryginalnej wartości.

Charles
źródło
1
Ponieważ twoja odpowiedź nie była w żaden sposób pomocna. „Podsumowując:! Metody zmieniają tylko wartość obiektu, do którego są wywoływane”, po prostu nie jest prawdą.
Darwin,
@Darwin to robi zmienić wartość obiektu. !mutuje obiekt zamiast zwracać zmodyfikowaną kopię.
Charles,
Jak myślisz, co to robi? User.create!
Darwin,
@Darwin w jakim kontekście? ActiveRecord?
Charles,
Tak, ActiveRecord.
Darwin,