Ruby Koans: ¿Por qué convertir una lista de símbolos en cadenas?

85

Me refiero a esta prueba en about_symbols.rb en Ruby Koans https://github.com/edgecase/ruby_koans/blob/master/src/about_symbols.rb#L26

def test_method_names_become_symbols
  symbols_as_strings = Symbol.all_symbols.map { |x| x.to_s }
  assert_equal true, symbols_as_strings.include?("test_method_names_become_symbols")
end


  # THINK ABOUT IT:
  #
  # Why do we convert the list of symbols to strings and then compare
  # against the string value rather than against symbols?

¿Por qué exactamente tenemos que convertir esa lista en cadenas primero?

codificador
fuente

Respuestas:

110

Esto tiene que ver con cómo funcionan los símbolos. Para cada símbolo, solo existe uno de ellos. Detrás de escena, un símbolo es solo un número al que se hace referencia por un nombre (que comienza con dos puntos). Por lo tanto, al comparar la igualdad de dos símbolos, está comparando la identidad del objeto y no el contenido del identificador que se refiere a este símbolo.

Si tuviera que hacer la prueba simple : prueba == "prueba" , será falso. Por lo tanto, si reuniera todos los símbolos definidos hasta ahora en una matriz, primero tendría que convertirlos en cadenas antes de compararlos. No puede hacer esto de la manera opuesta (primero convierta la cadena que desea comparar en un símbolo) porque hacerlo crearía la única instancia de ese símbolo y "contaminaría" su lista con el símbolo que está probando para su existencia.

Espero que ayude. Esto es un poco extraño, porque tienes que probar la presencia de un símbolo sin crear accidentalmente ese símbolo durante la prueba. Por lo general, no ve un código como ese.

Acerca de Ruby
fuente
2
Tenga en cuenta que una mejor manera de hacer esto de manera segura es asignar la salida de Symbol.all_symbolsa una variable y luego probar su inclusión. Los símbolos son más rápidos en la comparación y está evitando convertir miles de símbolos en cadenas.
coreyward
4
Eso todavía tiene el problema de crear el símbolo que no se puede destruir. Cualquier prueba futura para ese símbolo se arruinará. Pero esto es solo un Koan, no tiene que tener mucho sentido o ser rápido, solo demuestra cómo funcionan los símbolos.
AboutRuby
2
Esta respuesta no me funciona. Si estamos buscando la existencia de un símbolo, ¿por qué especificamos un argumento de cadena para? include?Si especificamos :test_method_names_become_symbols, no tendríamos que convertir todos esos símbolos en cadenas.
Isaac Rabinovitch
3
Isaac, por el problema identificado, que es que especificar :test_method_names_become_symbolsen la comparación lo creará, por lo que la comparación siempre será cierta. Al convertir all_symbolsa cadenas y comparar cadenas, podemos distinguir si el símbolo existía antes de la comparación.
Stephen
3
Tanto que no entiendo. Si hago >> array_of_symbols = Symbol.all_symbols, entonces >> array_of_symbols.include? (: Not_yet_used), obtengo falso, y obtengo verdadero para algo que se definió, así que no entiendo por qué la conversión a cadenas es necesaria .
codenoob
75

Porque si lo haces:

assert_equal true, all_symbols.include?(:test_method_names_become_symbols)

Puede (dependiendo de su implementación de ruby) ser verdadero automáticamente, porque :test_method_names_become_symbolscrea el símbolo. Consulte este informe de errores .

Andrew Grimm
fuente
1
Entonces, la respuesta aceptada es incorrecta (o eso me parece a mí).
Isaac Rabinovitch
3
Isaac, la otra respuesta no es incorrecta, pero no se explica de manera concisa. sin lugar a duda. De todos modos, puede verificar lo que dice Andrew con lo siguiente: assert_equal true, Symbol.all_symbols.include? (: Abcdef) Esto siempre pasará (al menos, lo hace para mí) independientemente del símbolo. Supongo que una lección es que no intente utilizar la presencia / ausencia de símbolos como una bandera booleana.
Stephen
1
Mirando esta pregunta, que todavía me interesa, 2 años después, realmente aprecio esta respuesta y los comentarios. Creo que Isaac tiene razón, esta es la respuesta que explica más claramente por qué la conversión a cadenas puede ser el camino a seguir, aunque creo que podría poner el paso intermedio (almacenar todos los símbolos antes de la comparación) para que funcione mejor.
codenoob
3

Ambas respuestas anteriores son correctas, pero a la luz de la pregunta de Karthik anterior, pensé que publicaría una prueba que ilustra cómo se puede pasar con precisión un símbolo al includemétodo.

def test_you_create_a_new_symbol_in_the_test
  array_of_symbols = []
  array_of_symbols << Symbol.all_symbols
  all_symbols = Symbol.all_symbols.map {|x| x}
  assert_equal false, array_of_symbols.include?(:this_should_not_be_in_the_symbols_collection)  #this works because we stored all symbols in an array before creating the symbol :this_should_not_be_in_the_symbols_collection in the test
  assert_equal true, all_symbols.include?(:this_also_should_not_be_in_the_symbols_collection) #This is the case noted in previous answers...here we've created a new symbol (:this_also_should_not_be_in_the_symbols_collection) in the test and then mapped all the symbols for comparison. Since we created the symbol before querying all_symbols, this test passes.
end

Una nota adicional sobre los Koans: utilice putsdeclaraciones y pruebas personalizadas si no comprende nada. Por ejemplo, si ve:

string = "the:rain:in:spain"
words = string.split(/:/)

y no tengo idea de lo que wordspodría ser, agregue la línea

puts words

y ejecutar rakeen la línea de comandos. Del mismo modo, las pruebas como la que agregué anteriormente pueden ser útiles para comprender algunos de los matices de Ruby.

aceofbassgreg
fuente
Mirando esta pregunta, que todavía me interesa, 2 años después, realmente aprecio esta respuesta y los comentarios.
codenoob