¿Por qué usar símbolos como claves hash en Ruby?


Muchas veces la gente usa símbolos como claves en un hash Ruby.

¿Cuál es la ventaja sobre el uso de una cadena?

Ej:

hash[:name]

Vs.

hash['name']
Author: Max, 2011-11-19

4 answers

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 Ruby 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 el mismo id de objeto.

Las cadenas por otro lado son mutables, se pueden cambiar en cualquier momento. Esto implica que Ruby necesita almacenar cada cadena que mencione a lo largo de su código fuente en su entidad separada, por ejemplo, si tiene una cadena "nombre" varias veces mencionada en su código fuente, Ruby necesita almacenar todo esto en objetos String separados, porque podrían cambiar más adelante (esa es la naturaleza de una string Ruby).

Si usa una cadena como 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 usas un símbolo como clave Hash, está implícito que es inmutable, por lo que Ruby puede básicamente simplemente haga una comparación de la función (hash del) object-id contra los (hash) object-id de las claves que ya están almacenadas en el Hash. (mucho más rápido)

Desventaja: Cada símbolo consume una ranura en la tabla de símbolos del intérprete Ruby, que nunca se libera. Los símbolos nunca se recogen en la basura. Por lo tanto, un caso de esquina es cuando tiene un gran número de símbolos (por ejemplo, los generados automáticamente). En ese caso, debe evaluar cómo esto afecta el tamaño de su Ruby interprete.

Notas:

Si hace comparaciones de cadenas, Ruby puede comparar símbolos solo por sus id de objeto, sin tener que evaluarlos. Eso es mucho más rápido que comparar cadenas, que necesitan ser evaluadas.

Si accedes a un hash, Ruby siempre aplica una función hash para calcular una "clave hash" a partir de cualquier clave que utilices. Puedes imaginar algo como un MD5-hash. Y luego Ruby compara esas "claves hash" entre sí.

Largo respuesta:

Http://www.reactive.io/tips/2009/01/11/the-difference-between-ruby-symbols-and-strings

Http://www.randomhacks.net/articles/2007/01/20/13-ways-of-looking-at-a-ruby-symbol

 201
Author: Tilo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-19 21:16:29

La razón es la eficiencia, con múltiples ganancias sobre una cadena:

  1. Los símbolos son inmutables, por lo que la pregunta "¿ qué sucede si la clave cambia?"no necesita ser preguntado.
  2. Las cadenas se duplican en el código y normalmente ocupan más espacio en la memoria.
  3. Las búsquedas de Hash deben calcular el hash de las claves para compararlas. Esto es O(n) para Cadenas y constante para Símbolos.

Además, Ruby 1.9 introdujo una sintaxis simplificada solo para hash con symbols keys (e. g. h.merge(foo: 42, bar: 6)), y Ruby 2.0 tiene argumentos de palabra clave que funcionan solo para las claves de símbolo.

Notas:

1) Puede que te sorprenda saber que Ruby trata las claves String de manera diferente a cualquier otro tipo. De hecho:

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 las 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 ocurrencias de :bar en un programa. Antes de Ruby 2.2, era una mala idea crear constantemente nuevos Symbols que nunca fueron reutilizados, ya que permanecerían en la tabla de búsqueda de símbolos globales para siempre. Ruby 2.2 recogerá basura de ellos, así que no te preocupes.

3) En realidad, calcular el hash para un Símbolo no tomó ningún tiempo en Ruby 1.8.x, ya que el 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 hashes cambian de una sesión a otra (incluidos los de Symbols):

:bar.hash # => some number that will be different next time Ruby 1.9 is ran
 21
Author: Marc-André Lafortune,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-20 18:31:23

Re: ¿cuál es la ventaja sobre el uso de una cadena?

  • Estilo: es la forma de Rubí
  • (Muy) búsquedas de valor ligeramente más rápidas ya que hashing un símbolo es equivalente a hashing un entero vs hashing una cadena.

  • Desventaja: consume una ranura en la tabla de símbolos del programa que nunca se libera.

 6
Author: Larry K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-18 21:39:58

Estaría muy interesado en un seguimiento de las cadenas congeladas introducidas en Ruby 2.x.

Cuando se trata de numerosas cadenas que provienen de una entrada de texto (estoy pensando en HTTP params o payload, 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 tu "vocabulario" de negocios), me gusta pensar que congelarlos puede marcar la diferencia. No he hecho ningún benchmark todavía, pero supongo que lo haría estar cerca de los símbolos de rendimiento.

 0
Author: jlecour,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-31 17:25:24