Respuestas:
TL; DR:
El uso de símbolos no solo ahorra tiempo al hacer comparaciones, sino que también ahorra memoria, ya que solo se almacenan una vez.
Los símbolos de rubí son inmutables (no se pueden cambiar), lo que hace que buscar algo sea mucho más fácil
Respuesta corta (ish):
El uso de símbolos no solo ahorra tiempo al hacer comparaciones, sino que también ahorra memoria, ya que solo se almacenan una vez.
Los símbolos en Ruby son básicamente "cadenas inmutables" ... eso significa que no se pueden cambiar, e implica que el mismo símbolo cuando se hace referencia muchas veces a lo largo de su código fuente, siempre se almacena como la misma entidad, por ejemplo, tiene la misma identificación de objeto .
Las cadenas, por otro lado, son mutables , se pueden cambiar en cualquier momento. Esto implica que Ruby necesita almacenar cada cadena que menciona a lo largo de su código fuente en su entidad separada, por ejemplo, si tiene un "nombre" de cadena mencionado varias veces en su código fuente, Ruby necesita almacenar todo esto en objetos de cadena separados, porque podría cambiar más adelante (esa es la naturaleza de una cadena Ruby).
Si usa una cadena como una clave Hash, Ruby necesita evaluar la cadena y mirar su contenido (y calcular una función hash en eso) y comparar el resultado con los valores (hash) de las claves que ya están almacenadas en el Hash .
Si usa un símbolo como una clave Hash, está implícito que es inmutable, por lo que Ruby básicamente puede hacer una comparación de la (función hash del) id-objeto contra los (id. Hash) de las claves que ya están almacenadas en el hachís. (mucho mas rápido)
Desventaja: cada símbolo consume un espacio en la tabla de símbolos del intérprete de Ruby, que nunca se libera. Los símbolos nunca se recogen basura. Entonces, un caso de esquina es cuando tiene una gran cantidad de símbolos (por ejemplo, los generados automáticamente). En ese caso, debe evaluar cómo esto afecta el tamaño de su intérprete de Ruby.
Notas:
Si hace comparaciones de cadenas, Ruby puede comparar símbolos solo por sus identificadores de objeto, sin tener que evaluarlos. Eso es mucho más rápido que comparar cadenas, que deben evaluarse.
Si accede a un hash, Ruby siempre aplica una función hash para calcular una "clave hash" a partir de cualquier tecla que utilice. Puedes imaginar algo como un MD5-hash. Y luego Ruby compara esas "claves hash" entre sí.
Respuesta larga:
La razón es la eficiencia, con múltiples ganancias sobre una cadena:
O(n)
para cadenas y constante para símbolos.Además, Ruby 1.9 introdujo una sintaxis simplificada solo para hash con teclas de símbolos (por ejemplo h.merge(foo: 42, bar: 6)
), y Ruby 2.0 tiene argumentos de palabras clave que funcionan solo para teclas de símbolos.
Notas :
1) Es posible que se sorprenda al saber que Ruby trata las String
claves de manera diferente que cualquier otro tipo. En efecto:
s = "foo"
h = {}
h[s] = "bar"
s.upcase!
h.rehash # must be called whenever a key changes!
h[s] # => nil, not "bar"
h.keys
h.keys.first.upcase! # => TypeError: can't modify frozen string
Solo para claves de cadena, Ruby usará una copia congelada en lugar del objeto en sí.
2) Las letras "b", "a" y "r" se almacenan solo una vez para todas las apariciones de :bar
un programa. Antes de Ruby 2.2, era una mala idea crear constantemente nuevos Symbols
que nunca se reutilizaran, ya que permanecerían en la tabla de búsqueda de símbolos global para siempre. Ruby 2.2 recogerá basura, así que no te preocupes.
3) En realidad, calcular el hash para un Símbolo no tardó en Ruby 1.8.x, ya que la ID del objeto se usó directamente:
:bar.object_id == :bar.hash # => true in Ruby 1.8.7
En Ruby 1.9.x, esto ha cambiado a medida que los hash cambian de una sesión a otra (incluidas las de Symbols
):
:bar.hash # => some number that will be different next time Ruby 1.9 is ran
Re: ¿cuál es la ventaja sobre el uso de una cadena?
Búsqueda de valores (muy) ligeramente más rápidos, ya que el hashing de un símbolo es equivalente a un hash de un entero vs un hash de una cadena.
Desventaja: consume un espacio en la tabla de símbolos del programa que nunca se publica.
Estaría muy interesado en un seguimiento sobre cadenas congeladas introducidas en Ruby 2.x.
Cuando maneja numerosas cadenas que provienen de una entrada de texto (estoy pensando en parámetros HTTP o carga útil, a través de Rack, por ejemplo), es mucho más fácil usar cadenas en todas partes.
Cuando tratas con docenas de ellos pero nunca cambian (si son el "vocabulario" de tu negocio), me gusta pensar que congelarlos puede marcar la diferencia. Todavía no he hecho ningún punto de referencia, pero supongo que estaría cerca del rendimiento de los símbolos.