Si hay al menos dos instancias de la misma cadena en mi secuencia de comandos, ¿debería usar un símbolo?
Si hay al menos dos instancias de la misma cadena en mi secuencia de comandos, ¿debería usar un símbolo?
Respuestas:
Una regla general simple es usar símbolos cada vez que necesite identificadores internos. Para Ruby <2.2 solo use símbolos cuando no se generen dinámicamente, para evitar pérdidas de memoria.
La única razón para no usarlos para identificadores que se generan dinámicamente es por problemas de memoria.
Esta pregunta es muy común porque muchos lenguajes de programación no tienen símbolos, solo cadenas y, por lo tanto, las cadenas también se utilizan como identificadores en su código. Usted debe ser preocuparse por lo que los símbolos están destinados a ser , no sólo cuando se debe utilizar símbolos . Los símbolos están destinados a ser identificadores. Si sigue esta filosofía, lo más probable es que haga las cosas bien.
Existen varias diferencias entre la implementación de símbolos y cadenas. Lo más importante de los símbolos es que son inmutables . Esto significa que nunca cambiará su valor. Debido a esto, los símbolos se instancian más rápido que las cadenas y algunas operaciones como comparar dos símbolos también son más rápidas.
El hecho de que un símbolo sea inmutable le permite a Ruby usar el mismo objeto cada vez que hace referencia al símbolo, ahorrando memoria. Por lo tanto, cada vez que el intérprete lo lee :my_key
, puede tomarlo de la memoria en lugar de crear una instancia nuevamente. Esto es menos costoso que inicializar una nueva cadena cada vez.
Puede obtener una lista de todos los símbolos que ya están instanciados con el comando Symbol.all_symbols
:
symbols_count = Symbol.all_symbols.count # all_symbols is an array with all
# instantiated symbols.
a = :one
puts a.object_id
# prints 167778
a = :two
puts a.object_id
# prints 167858
a = :one
puts a.object_id
# prints 167778 again - the same object_id from the first time!
puts Symbol.all_symbols.count - symbols_count
# prints 2, the two objects we created.
Para las versiones de Ruby anteriores a 2.2, una vez que se crea una instancia de un símbolo, esta memoria nunca volverá a estar libre . La única forma de liberar memoria es reiniciando la aplicación. Por lo tanto, los símbolos también son una causa importante de pérdidas de memoria cuando se usan incorrectamente. La forma más sencilla de generar una pérdida de memoria es utilizar el método to_sym
en los datos de entrada del usuario, ya que estos datos siempre cambiarán, una nueva porción de la memoria se utilizará para siempre en la instancia del software. Ruby 2.2 introdujo el recolector de basura de símbolos , que libera símbolos generados dinámicamente, por lo que las pérdidas de memoria generadas al crear símbolos dinámicamente ya no son una preocupación.
Respondiendo tu pregunta:
¿Es cierto que tengo que usar un símbolo en lugar de una cadena si hay al menos dos cadenas iguales en mi aplicación o script?
Si lo que está buscando es un identificador para usarlo internamente en su código, debe usar símbolos. Si está imprimiendo una salida, debe ir con cadenas, incluso si aparece más de una vez, incluso asignando dos objetos diferentes en la memoria.
Este es el razonamiento:
@AlanDert: si utilizo muchas veces algo como% input {type:: checkbox} en el código haml, ¿qué debo usar como casilla de verificación?
Yo: si.
@AlanDert: Pero para imprimir un símbolo en una página html, debería convertirse en una cadena, ¿no? ¿Cuál es el punto de usarlo entonces?
¿Cuál es el tipo de entrada? ¿Un identificador del tipo de entrada que desea utilizar o algo que desea mostrar al usuario?
Es cierto que se convertirá en código HTML en algún momento, pero en el momento en que está escribiendo esa línea de su código, significa que es un identificador: identifica qué tipo de campo de entrada necesita. Por lo tanto, se usa una y otra vez en su código, y siempre tiene la misma "cadena" de caracteres que el identificador y no generará una pérdida de memoria.
Dicho esto, ¿por qué no evaluamos los datos para ver si las cadenas son más rápidas?
Este es un punto de referencia simple que creé para esto:
require 'benchmark'
require 'haml'
str = Benchmark.measure do
10_000.times do
Haml::Engine.new('%input{type: "checkbox"}').render
end
end.total
sym = Benchmark.measure do
10_000.times do
Haml::Engine.new('%input{type: :checkbox}').render
end
end.total
puts "String: " + str.to_s
puts "Symbol: " + sym.to_s
Tres salidas:
# first time
String: 5.14
Symbol: 5.07
#second
String: 5.29
Symbol: 5.050000000000001
#third
String: 4.7700000000000005
Symbol: 4.68
Entonces, usar símbolos es un poco más rápido que usar cadenas. ¿Porqué es eso? Depende de la forma en que se implemente HAML. Necesitaría hackear un poco el código HAML para verlo, pero si sigues usando símbolos en el concepto de un identificador, tu aplicación será más rápida y confiable. Cuando surjan preguntas, compárelo y obtenga sus respuestas.
En pocas palabras, un símbolo es un nombre, compuesto de caracteres, pero inmutable. Una cadena, por el contrario, es un contenedor ordenado de caracteres, cuyo contenido puede cambiar.
Para comparar dos cadenas, potencialmente necesitamos observar cada carácter. Para dos cadenas de longitud N, esto requerirá N + 1 comparaciones (a las que los científicos informáticos se refieren como "tiempo O (N)").
def string_comp str1, str2
return false if str1.length != str2.length
for i in 0...str1.length
return false if str1[i] != str2[i]
end
return true
end
string_comp "foo", "foo"
Pero dado que cada aparición de: foo se refiere al mismo objeto, podemos comparar los símbolos mirando los ID de los objetos. Podemos hacer esto con una sola comparación (a la que los científicos informáticos se refieren como "tiempo O (1)").
def symbol_comp sym1, sym2
sym1.object_id == sym2.object_id
end
symbol_comp :foo, :foo
En C ++, podemos usar "enumeraciones" para representar familias de constantes relacionadas:
enum BugStatus { OPEN, CLOSED };
BugStatus original_status = OPEN;
BugStatus current_status = CLOSED;
Pero debido a que Ruby es un lenguaje dinámico, no nos preocupamos por declarar un tipo de BugStatus o realizar un seguimiento de los valores legales. En cambio, representamos los valores de enumeración como símbolos:
original_status = :open
current_status = :closed
3.Un símbolo Ruby es un nombre único y constante
En Ruby, podemos cambiar el contenido de una cadena:
"foo"[0] = ?b # "boo"
Pero no podemos cambiar el contenido de un símbolo:
:foo[0] = ?b # Raises an error
Al pasar argumentos de palabras clave a una función de Ruby, especificamos las palabras clave usando símbolos:
# Build a URL for 'bug' using Rails.
url_for :controller => 'bug',
:action => 'show',
:id => bug.id
Normalmente, usaremos símbolos para representar las claves de una tabla hash:
options = {}
options[:auto_save] = true
options[:show_comments] = false
Aquí hay un buen punto de referencia de cadenas vs símbolos que encontré en codecademy:
require 'benchmark'
string_AZ = Hash[("a".."z").to_a.zip((1..26).to_a)]
symbol_AZ = Hash[(:a..:z).to_a.zip((1..26).to_a)]
string_time = Benchmark.realtime do
1000_000.times { string_AZ["r"] }
end
symbol_time = Benchmark.realtime do
1000_000.times { symbol_AZ[:r] }
end
puts "String time: #{string_time} seconds."
puts "Symbol time: #{symbol_time} seconds."
La salida es:
String time: 0.21983 seconds.
Symbol time: 0.087873 seconds.
utilizar símbolos como identificadores de clave hash
{key: "value"}
los símbolos le permiten llamar al método en un orden diferente
def write (archivo :, datos :, modo: "ascii") # eliminado por brevedad final escribir (datos: 123, archivo: "test.txt")
congelar para mantener como una cadena y ahorrar memoria
label = 'My Label'.freeze
/
(despuésstrings
) del enlace. Aquí está: www.reactive.io/tips/2009/01/11/the-difference-between-ruby- symbols-and-strings