Respostas:
TL; DR:
O uso de símbolos não apenas economiza tempo ao fazer comparações, mas também economiza memória, porque eles são armazenados apenas uma vez.
Os símbolos Ruby são imutáveis (não podem ser alterados), o que facilita muito a busca de algo
Resposta curta (ish):
O uso de símbolos não apenas economiza tempo ao fazer comparações, mas também economiza memória, porque eles são armazenados apenas uma vez.
Os símbolos no Ruby são basicamente "strings imutáveis" . Isso significa que eles não podem ser alterados e implica que o mesmo símbolo, quando referenciado muitas vezes em todo o código-fonte, é sempre armazenado como a mesma entidade, por exemplo, tem o mesmo ID de objeto .
As strings, por outro lado, são mutáveis , podem ser alteradas a qualquer momento. Isso implica que o Ruby precise armazenar cada string mencionada em todo o código-fonte em sua entidade separada; por exemplo, se você tiver um "nome" da string várias vezes mencionado no código-fonte, o Ruby precisará armazenar tudo isso em objetos String separados, porque eles pode mudar mais tarde (essa é a natureza de uma string Ruby).
Se você usar uma string como chave Hash, Ruby precisará avaliar a string e analisar seu conteúdo (e calcular uma função hash) e comparar o resultado com os valores (hash) das chaves que já estão armazenadas no Hash. .
Se você usa um símbolo como chave Hash, está implícito que é imutável, então Ruby pode basicamente fazer uma comparação entre a (função hash da) identificação de objeto e a identificação de objeto (com hash) de chaves que já estão armazenadas em o Hash. (muito mais rapido)
Desvantagem: cada símbolo consome um slot na tabela de símbolos do intérprete Ruby, que nunca é liberado. Os símbolos nunca são coletados no lixo. Portanto, uma caixa de esquina é quando você tem um grande número de símbolos (por exemplo, símbolos gerados automaticamente). Nesse caso, você deve avaliar como isso afeta o tamanho do seu intérprete Ruby.
Notas:
Se você fizer comparações de strings, o Ruby poderá comparar símbolos apenas pelos respectivos IDs de objeto, sem precisar avaliá-los. Isso é muito mais rápido do que comparar seqüências de caracteres, que precisam ser avaliadas.
Se você acessar um hash, o Ruby sempre aplicará uma função de hash para calcular uma "chave de hash" de qualquer chave que você usar. Você pode imaginar algo como um hash MD5. E então Ruby compara essas "chaves de hash" umas com as outras.
Resposta longa:
O motivo é a eficiência, com vários ganhos sobre uma String:
O(n)
para Strings e constante para Symbols.Além disso, o Ruby 1.9 introduziu uma sintaxe simplificada apenas para hash com chaves de símbolos (por exemplo h.merge(foo: 42, bar: 6)
), e o Ruby 2.0 possui argumentos de palavras - chave que funcionam apenas para chaves de símbolos.
Notas :
1) Você pode se surpreender ao saber que Ruby trata as String
teclas de maneira diferente de qualquer outro tipo. De fato:
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
Apenas para chaves de seqüência de caracteres, Ruby usará uma cópia congelada em vez do próprio objeto.
2) As letras "b", "a" e "r" são armazenadas apenas uma vez para todas as ocorrências de :bar
um programa. Antes do Ruby 2.2, era uma má ideia criar constantemente novos Symbols
que nunca eram reutilizados, pois eles permaneceriam na tabela de pesquisa de símbolos global para sempre. O Ruby 2.2 irá coletá-los no lixo, então não se preocupe.
3) Na verdade, o cálculo do hash para um símbolo não demorou muito no Ruby 1.8.x, pois o ID do objeto era usado diretamente:
:bar.object_id == :bar.hash # => true in Ruby 1.8.7
No Ruby 1.9.x, isso mudou conforme os hashes mudam de uma sessão para outra (incluindo as de Symbols
):
:bar.hash # => some number that will be different next time Ruby 1.9 is ran
Re: qual é a vantagem sobre o uso de uma string?
Pesquisas de valor (muito) um pouco mais rápidas, pois hash de um símbolo é equivalente a hash de um número inteiro versus hash de uma string.
Desvantagem: consome um slot na tabela de símbolos do programa que nunca é liberado.
Eu ficaria muito interessado em acompanhar as seqüências congeladas introduzidas no Ruby 2.x.
Quando você lida com várias seqüências de caracteres provenientes de uma entrada de texto (estou pensando em parâmetros HTTP ou carga útil, através do Rack, por exemplo), é muito mais fácil usar seqüências de caracteres em qualquer lugar.
Quando você lida com dezenas deles, mas eles nunca mudam (se é o "vocabulário" da sua empresa)), gosto de pensar que congelá-los pode fazer a diferença. Ainda não fiz nenhum benchmark, mas acho que isso fecharia o desempenho dos símbolos.