2009-03-04 15 views
418

In Ruby alcuni metodi hanno un punto interrogativo (?) che fa una domanda come include? che chiede se l'oggetto in questione è incluso, quindi restituisce un vero/falso.Perché i punti esclamativi vengono utilizzati nei metodi Ruby?

Ma perché alcuni metodi hanno punti esclamativi (!) dove altri non lo fanno?

Che cosa significa?

+15

sinonimo: bang, punto esclamativo – prusswan

+15

La risposta accettata deve essere modificato in http://stackoverflow.com/a/612653/109618. Vedi http://www.wobblini.net/bang.txt e http://www.ruby-forum.com/topic/176830#773946 - "Il segno del botto significa" la versione bang è più pericolosa del suo non controparte bang; maneggia con cura "" -Matz –

+2

Il metodo bang sarebbe un'ottima scelta di design se ** solo ** e ** tutti ** i metodi bang fossero pericolosi. Purtroppo non lo sono, e quindi diventa un esercizio frustrante nel memorizzare ciò che è e non è mutabile. –

risposta

498

In generale, i metodi che terminano con ! indicano che il metodo modificherà l'oggetto chiamato . Ruby li chiama come "metodi pericolosi" perché cambiano lo stato a cui potrebbe fare riferimento un altro utente. Ecco un semplice esempio per le stringhe:

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

Questo stamperà:

a string 

Nelle librerie standard, ci sono un sacco di posti vedrete coppie di metodi di nome simile, quello con la ! e uno senza. Quelli senza sono chiamati "metodi sicuri" e restituiscono una copia dell'originale con le modifiche applicate a la copia, con il numero di telefono invariato. Ecco lo stesso esempio senza la !:

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 

Questo uscite:

A STRING 
a string 

tenere a mente questo è solo una convenzione, ma un sacco di classi di Ruby seguirla. Ti aiuta anche a tenere traccia di ciò che viene modificato nel tuo codice.

+2

Ci sono anche casi come uscita contro uscita! e (nei binari) salva contro salva! –

+13

Fai molta attenzione: molte librerie più piccole non seguono questa convenzione.Se accadono cose strane, spesso sostituendo obj.whatever! con obj = obj.whatever! lo risolve Molto frustrante. –

+73

bang viene anche utilizzato per metodi che generano un'eccezione quando il metodo senza, ad esempio: 'save' e' save! 'In' ActiveRecord' – ecoologic

23

! in genere significa che il metodo agisce sull'oggetto anziché restituire un risultato. Dal libro Programming Ruby: "!"

metodi che sono "pericolosi", o modificare il ricevitore, potrebbe essere chiamato con un finale.

56

Questa convenzione di denominazione viene revocata da Scheme.

1.3.5 denominazione convenzioni

Per convenzione, i nomi di procedure che restituiscono sempre un valore booleano di solito finiscono in ``? ''. Tali procedure sono chiamate predicati.

Per convenzione, i nomi di procedure che memorizzano i valori in precedenza posizioni assegnate (vedere paragrafo 3.4) di solito finiscono in ``! ''. Tali procedure sono chiamate procedure di mutazione. Con la convenzione , il valore restituito da una procedura di mutazione non è specificato.

+1

+1 a questa risposta poiché ha una documentazione che fornisce spiegazioni ragionevoli per il! utilizzo. Davvero buona risposta Steven – DavidSilveira

+0

Grazie @DavidSilveira! –

118

Il punto esclamativo indica molte cose ea volte non si può dire molto da esso se non "questo è pericoloso, attenzione".

Come altri hanno già detto, nei metodi standard viene spesso utilizzato per indicare un metodo che causa la mutazione di un oggetto stesso, ma non sempre. Nota che molti metodi standard cambiano il loro ricevitore e non hanno un punto esclamativo (pop, shift, clear) e alcuni metodi con punti esclamativi non cambiano il loro ricevitore (exit!). Vedere this article per esempio.

Altre librerie possono utilizzarlo in modo diverso. In Rails, un punto esclamativo indica spesso che il metodo genererà un'eccezione in caso di errore anziché fallire in modo silenzioso.

È una convenzione di denominazione ma molte persone lo usano in modi leggermente diversi. Nel tuo codice una buona regola generale è quella di usarlo ogni volta che un metodo sta facendo qualcosa di "pericoloso", specialmente quando esistono due metodi con lo stesso nome e uno di essi è più "pericoloso" dell'altro. "Pericoloso" può significare quasi tutto comunque.

13

Da themomorohoax.com:

un botto può utilizzato nei modi di seguito, in ordine di mia preferenza personale.

1) Un metodo record attivo genera un errore se il metodo non fa quello che dice che lo farà.

2) Un metodo record attivo salva il record o un metodo consente di risparmiare un oggetto (per esempio strip!)

3) Metodo fa qualcosa “in più”, come post da qualche parte, o fa qualche azione.

Il punto è: usare solo un botto quando hai veramente pensato se è necessario, per salvare altri sviluppatori il fastidio di dover verifica perché si sta utilizzando un botto.

Il botto offre due spunti agli altri sviluppatori.

1) che non è necessario salvare l'oggetto dopo aver chiamato il metodo .

2) quando si chiama il metodo, il db sta per essere modificato.

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

6

semplice spiegazione:

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. 

Ma se mai chiamato un metodo downcase! nella spiegazione di cui sopra, foo cambierebbe per downcase in modo permanente. downcase! non restituisce un nuovo oggetto stringa ma sostituisce la stringa in posizione, modificando totalmente lo foo in downcase. Suggerisco di non usare downcase! a meno che non sia assolutamente necessario.

13

È più preciso dire che i metodi con un botto! sono la versione più dangerous o surprising. Esistono molti metodi che mutano senza un Bang come .destroy e in generale i metodi hanno solo i bang dove esiste un'alternativa più sicura nella lib core.

Per esempio, l'Array abbiamo .compact e .compact!, entrambi i metodi mutano la matrice, ma .compact! rendimenti pari a zero, invece di sé se non ci sono nulli di dell'array, che è più sorprendente di un semplice ritorno di sé.

L'unico metodo non-mutante che ho trovato con il botto è 's .exit! che è più sorprendente di .exit perché non si può prendere SystemExit mentre il processo si sta chiudendo Kernel.

Rails e ActiveRecord continuano questa tendenza in quanto utilizza il botto per effetti più "sorprendenti" come .create! che genera errori in caso di errore.

+0

Faccio voto per parlare di 'nil'. – Nakilon

0

In basso: i metodi ! cambiano semplicemente il valore dell'oggetto su cui sono chiamati, mentre un metodo senza ! restituisce un valore manipolato senza scrivere sull'oggetto su cui è stato chiamato il metodo.

Utilizzare solo ! se non si intende richiedere il valore originale memorizzato nella variabile su cui è stato chiamato il metodo.

preferisco fare qualcosa di simile:

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

O

foo = "word" 
puts foo.capitalize 

Invece di

foo = "word" 
foo.capitalize! 
puts foo 

Solo nel caso vorrei accedere nuovamente al valore originale.

+0

Perché è stato downvoted !? – Charles

+1

Perché la tua risposta non è stata utile in alcun modo. "In conclusione: i metodi! Cambiano semplicemente il valore dell'oggetto su cui sono chiamati" non è vero. – Darwin

+0

@Darwin _does_ modifica il valore dell'oggetto. '!' muta l'oggetto piuttosto che restituire una copia modificata. – Charles

0
! 

Mi piace pensare a questo come a un cambiamento esplosivo che distrugge tutto ciò che è accaduto prima. Botto o punto esclamativo significa che stai apportando una modifica permanente salvata nel codice.

Se si utilizza ad esempio il metodo di Ruby per la sostituzione globale gsub!, la sostituzione effettuata è permanente.

Un altro modo in cui puoi immaginarlo è aprire un file di testo e cercare e sostituire, seguito dal salvataggio. ! fa lo stesso nel tuo codice.

Un altro utile promemoria per chi viene dal mondo di bash è sed -i ha questo effetto simile di apportare modifiche permanenti salvate.

1

Chiamato "Metodi distruttivi" Tendono a cambiare la copia originale dell'oggetto a cui ci si riferisce.

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]