¿Por qué se usan signos de exclamación en los métodos de Ruby?

540

En Ruby, algunos métodos tienen un signo de interrogación ( ?) que hace una pregunta como include?esa pregunta si el objeto en cuestión está incluido, esto luego devuelve un verdadero / falso.

Pero, ¿por qué algunos métodos tienen signos de exclamación ( !) donde otros no?

Qué significa eso?

Lennie
fuente
21
sinónimo: explosión, signo de exclamación
prusswan
17
La respuesta aceptada debe cambiarse a stackoverflow.com/a/612653/109618 . Consulte wobblini.net/bang.txt y ruby-forum.com/topic/176830#773946 - "El signo de explosión significa" que la versión de explosión es más peligrosa que su contraparte sin explosión; manejar con cuidado "" -Matz
David J.
2
El método de explosión sería una gran opción de diseño si solo y todos los métodos de explosión fueran peligrosos. Lamentablemente no lo son, por lo que se convierte en un ejercicio frustrante para memorizar lo que es y no es mutable.
Damien Roche

Respuestas:

617

En general, los métodos que terminan en !indican que el método modificará el objeto al que está llamado . Ruby los llama " métodos peligrosos " porque cambian el estado al que alguien más podría tener una referencia. Aquí hay un ejemplo simple para cadenas:

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

Esto generará:

a string

En las bibliotecas estándar, hay muchos lugares donde verá pares de métodos con nombres similares, uno con !y otro sin él. Los que no se llaman "métodos seguros" y devuelven una copia del original con los cambios aplicados a la copia , con la persona que llama sin cambios. Aquí está el mismo ejemplo sin el !:

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

Esto produce:

A STRING
a string

Tenga en cuenta que esto es solo una convención, pero muchas clases de Ruby lo siguen. También lo ayuda a realizar un seguimiento de lo que se modifica en su código.

Todd Gamblin
fuente
2
¡También hay casos como salida versus salida! y (en rieles) guardar versus guardar!
Andrew Grimm
24
Tenga mucho cuidado: muchas bibliotecas más pequeñas no siguen esta convención. Si suceden cosas extrañas, a menudo reemplazando obj.whatever! con obj = obj.¡cualquier cosa! lo arregla Muy frustrante.
Sarah Mei
101
explosión también se utiliza para los métodos que elevan una excepción cuando el método sin no lo hace, por ejemplo: savey save!enActiveRecord
ecoologic
3
@AbhilashAK guardar! genera un error si no puede guardar. Esto se opone a que el guardado regular devuelva verdadero / falso.
BookOfGreg
31
@tgamblin Hay muchos métodos en Ruby que mutan sin flequillo. Incluso hay métodos raros que no mutan con una explosión, pero hacen algo sorprendente, como generar errores u omitir errores. Los golpes se usan para decir que esta es la versión más inusual del método y creo que esto debería reflejarse en su respuesta, ya que está marcado como correcto.
BookOfGreg
143

El signo de exclamación significa muchas cosas, y a veces no se puede distinguir mucho más que "esto es peligroso, tenga cuidado".

Como han dicho otros, en los métodos estándar a menudo se usa para indicar un método que hace que un objeto se mute a sí mismo, pero no siempre. Tenga en cuenta que muchos métodos estándar cambiar su receptor y no tienen un punto (exclamación pop, shift, clear), y algunos métodos con los signos de exclamación no cambian su receptor ( exit!). Ver este artículo por ejemplo.

Otras bibliotecas pueden usarlo de manera diferente. En Rails, un signo de exclamación a menudo significa que el método arrojará una excepción al fallo en lugar de fallar en silencio.

Es una convención de nomenclatura, pero muchas personas la usan de maneras sutilmente diferentes. En su propio código, una buena regla general es usarlo siempre que un método esté haciendo algo "peligroso", especialmente cuando existen dos métodos con el mismo nombre y uno de ellos es más "peligroso" que el otro. Sin embargo, "peligroso" puede significar casi cualquier cosa.

Brian Carper
fuente
75

Esta convención de nomenclatura se levanta de Scheme .

1.3.5 Convenciones de nomenclatura

Por convención, los nombres de los procedimientos que siempre devuelven un valor booleano generalmente terminan en ``? ''. Tales procedimientos se llaman predicados.

Por convención, los nombres de los procedimientos que almacenan valores en ubicaciones previamente asignadas (consulte la sección 3.4) generalmente terminan en ``! ''. Dichos procedimientos se denominan procedimientos de mutación. Por convención, el valor devuelto por un procedimiento de mutación no está especificado.

Steven Huwig
fuente
2
¡+1 a esta respuesta ya que tiene una documentación que brinda explicaciones razonables para! uso. Muy buena respuesta Steven
DavidSilveira
Gracias @DavidSilveira!
Steven Huwig
24

! normalmente significa que el método actúa sobre el objeto en lugar de devolver un resultado. Del libro Programming Ruby :

Los métodos que son "peligrosos" o que modifican el receptor, pueden ser nombrados con un "!" Al final.

pesto
fuente
18

¡Es más exacto decir que los métodos con un Bang! son la versión más peligrosa o sorprendente . Hay muchos métodos que mutan sin una explosión, como.destroy y en general, los métodos solo tienen explosiones donde existe una alternativa más segura en la biblioteca principal.

Por ejemplo, en Array tenemos .compacty .compact!, ambos métodos mutan la matriz, pero.compact! devuelve nil en lugar de self si no hay nil en la matriz, lo cual es más sorprendente que solo self.

El único método no mutante que he encontrado con una explosión es Kernelel .exit!que es más sorprendente que .exitporque no se puede atrapar SystemExitmientras el proceso se está cerrando.

Rails y ActiveRecord continúan esta tendencia, ya que utiliza la explosión para obtener más efectos 'sorprendentes' como los .create!que generan errores en caso de falla.

BookOfGreg
fuente
16

De themomorohoax.com:

Se puede usar una explosión de las siguientes maneras, en orden de preferencia personal.

1) Un método de registro activo genera un error si el método no hace lo que dice que hará.

2) Un método de registro activo guarda el registro o un método guarda un objeto (por ejemplo, ¡tira!)

3) Un método hace algo "extra", como publicaciones en algún lugar, o realiza alguna acción.

El punto es: solo usa una explosión cuando realmente hayas pensado si es necesario, para salvar a otros desarrolladores de la molestia de tener que verificar por qué estás usando una explosión.

La explosión proporciona dos pistas a otros desarrolladores.

1) que no es necesario guardar el objeto después de llamar al método.

2) cuando llama al método, se cambiará la base de datos.

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

Edward Castaño
fuente
6

Explicación 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.

Pero si alguna vez llamó a un método downcase!en la explicación anterior, foocambiaría a downcase permanentemente. downcase!no devolvería un nuevo objeto de cadena, sino que reemplazaría la cadena en su lugar, cambiando totalmente fooa minúscula. Te sugiero que no uses a downcase!menos que sea totalmente necesario.

Espejismo
fuente
1
!

Me gusta pensar en esto como un cambio explosivo que destruye todo lo que sucedió antes. Explosión o signo de exclamación significa que está realizando un cambio guardado permanente en su código.

Si usa, por ejemplo, el método de Ruby para la sustitución globalgsub! la sustitución que realice es permanente.

Otra forma de imaginarlo es abrir un archivo de texto y buscar y reemplazar, seguido de guardar. !hace lo mismo en tu código.

Otro recordatorio útil si vienes del mundo bash es que sed -itiene este efecto similar de hacer un cambio guardado permanente.

Charlie Wood
fuente
1

Los llamados "métodos destructivos" tienden a cambiar la copia original del objeto al que se refiere.

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

En !pocas palabras: los métodos simplemente cambian el valor del objeto al que se les llama, mientras que un método sin! devuelve un valor manipulado sin escribir sobre el objeto sobre el que se invocó el método.

Úselo solo !si no planea necesitar el valor original almacenado en la variable a la que llamó el método.

Prefiero hacer algo como:

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

O

foo = "word"
puts foo.capitalize

En vez de

foo = "word"
foo.capitalize!
puts foo

Por si acaso quisiera acceder al valor original nuevamente.

Charles
fuente
1
Porque su respuesta no fue útil de ninguna manera. "En pocas palabras: los métodos simplemente cambian el valor del objeto al que son llamados" simplemente no es cierto.
Darwin
@Darwin, cambia el valor del objeto. !muta el objeto en lugar de devolver una copia modificada.
Charles
Entonces, ¿qué crees que hace esto? User.create!
Darwin el
@Darwin en qué contexto? ActiveRecord?
Charles
Sí, ActiveRecord.
Darwin