TL; DR: ¿Habría una definición agnóstica del lenguaje de los Símbolos y una razón para tenerlos en otros idiomas?
Entonces, ¿por qué el creador de Ruby utilizó el concepto de symbols
en el lenguaje?
Pido esto desde la perspectiva de un programador no rubí. Aprendí muchos otros idiomas y no encontré en ninguno de ellos la necesidad de especificar si estaba tratando o no con lo que Ruby llama symbols
.
La pregunta principal es, ¿existe el concepto de symbols
en Ruby para el rendimiento, o simplemente algo que se necesita debido a la forma en que se escribe el idioma?
¿Sería un programa en Ruby más ligero y / o más rápido que su, digamos, Python o Javascript? Si es así, ¿sería por symbols
?
Dado que una de las intenciones de Ruby es ser fácil de leer y escribir para los humanos, ¿no podrían permitir que sus creadores facilitaran el proceso de codificación implementando esas mejoras en el propio intérprete (como podría ser en otros idiomas)?
Parece que todo el mundo quiere saber solo qué symbols
son y cómo usarlos, y no por qué están allí en primer lugar.
fuente
Respuestas:
El creador de Ruby, Yukihiro "Matz" Matsumoto, publicó una explicación sobre cómo Ruby fue influenciado por Lisp, Smalltalk, Perl (y Wikipedia dice que Ada y Eiffel también):
En cualquier compilador, administrará identificadores para funciones, variables, bloques con nombre, tipos, etc. Por lo general, los almacena en el compilador y los olvida en el ejecutable producido, excepto cuando agrega información de depuración.
En Lisp, dichos símbolos son recursos de primera clase, alojados en diferentes paquetes, lo que significa que puede agregar símbolos nuevos en tiempo de ejecución, vincularlos a diferentes tipos de objetos. Esto es útil cuando se metaprograma porque puede estar seguro de que no tendrá colisiones de nombres con otras partes del código.
Además, los símbolos son internados en el momento de la lectura y se pueden comparar por identidad, que es una forma eficiente de tener nuevos tipos de valores (como números, pero abstractos). Esto ayuda a escribir código donde usa valores simbólicos directamente, en lugar de definir sus propios tipos de enumeración respaldados por enteros. Además, cada símbolo puede contener datos adicionales. Así es como, por ejemplo, Emacs / Slime puede adjuntar metadatos de Emacs directamente a la lista de propiedades de un símbolo.
La noción de símbolo es central en Lisp. Eche un vistazo, por ejemplo, a PAIP (Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp, Norvig) para ver ejemplos detallados.
fuente
Bueno, ellos estrictamente "no tenían que", decidieron hacerlo. Además, tenga en cuenta que estrictamente hablando
Symbol
s no son parte del lenguaje, son parte de la biblioteca central. Ellos no tienen una sintaxis literal de nivel de idioma, sino que funcionarían igual de bien si había que construir con ellos llamandoSymbol::new
.No dijiste cuáles son esos "muchos otros idiomas", pero aquí hay un pequeño extracto de idiomas que tienen un
Symbol
tipo de datos como el de Ruby:También hay otros lenguajes que proporcionan las características de
Symbol
s en una forma diferente. En Java, por ejemplo, las características de Ruby'sString
se dividen en dos (en realidad tres) tipos:String
yStringBuilder
/StringBuffer
. Por otro lado, las características delSymbol
tipo de Ruby se pliegan en elString
tipo de Java : losString
s de Java pueden ser internados , las cadenas literales y lasString
s que son el resultado de expresiones constantes evaluadas en tiempo de compilación se internan automáticamente, losString
s generados dinámicamente se pueden internar llamando ElString.intern
método Un internoString
en Java es exactamente como unSymbol
en Ruby, pero no está implementado como un tipo separado, es solo un estado diferente que un JavaString
puede estar en. (Nota: en versiones anteriores de Ruby,String#to_sym
solía llamarseString#intern
y ese método todavía existe hoy en día como un alias heredado).Symbol
s son ante todo un tipo de datos con semántica específica . Estas semánticas también permiten implementar algunas operaciones de rendimiento (por ejemplo, pruebas rápidas de igualdad O (1)), pero ese no es el objetivo principal.Symbol
s no son necesarios en absoluto en el lenguaje Ruby, Ruby funcionaría bien sin ellos. Son puramente una función de biblioteca. Hay exactamente un lugar en el lenguaje que está vinculado aSymbol
s: unadef
expresión de definición de método se evalúa comoSymbol
denotando el nombre del método que se está definiendo. Sin embargo, ese es un cambio bastante reciente, antes de eso, el valor de retorno simplemente se dejó sin especificar. MRI simplemente evaluó anil
, Rubinius evaluó a unRubinius::CompiledMethod
objeto, y así sucesivamente. También sería posible evaluar a unUnboundMethod
... o simplemente aString
.No estoy seguro de lo que estás preguntando aquí. El rendimiento es principalmente una cuestión de calidad de implementación, no de lenguaje. Además, Node ni siquiera es un lenguaje, es un marco de E / S creado para ECMAScript. Al ejecutar un script equivalente en IronPython y MRI, es probable que IronPython sea más rápido. Al ejecutar un script equivalente en CPython y JRuby + Truffle, es probable que JRuby + Truffle sea más rápido. Esto no tiene nada que ver con
Symbol
s, sino con la calidad de la implementación: JRuby + Truffle tiene un compilador de optimización agresiva, además de toda la maquinaria de optimización de una JVM de alto rendimiento, CPython es un intérprete simple.No.
Symbol
s no son una optimización del compilador. Son un tipo de datos separado con una semántica específica. No son como los flonums de YARV , que son una optimización interna privada paraFloat
s. La situación no es la misma que paraInteger
,Bignum
yFixnum
, que debería ser un detalle de optimización interna privada invisible, pero desafortunadamente no lo es. (Esto finalmente va a ser fijado en Ruby 2.4, que eliminaFixnum
yBignum
y las hojas sóloInteger
.)Hacerlo de la manera en que lo hace Java, ya que un estado especial de
String
s normal significa que siempre debe ser cauteloso acerca de si susString
s están en ese estado especial y bajo qué circunstancias están automáticamente en ese estado especial y cuándo no. Esa es una carga mucho más alta que simplemente tener un tipo de datos separado.Symbol
es un tipo de datos que denota el concepto de nombre o etiqueta .Symbol
Los s son objetos de valor , inmutables, generalmente inmediatos (si el lenguaje distingue tal cosa), sin estado y sin identidad.Symbol
También se garantiza que dos s que son iguales son idénticos, en otras palabras, dosSymbol
s que son iguales son en realidad el mismoSymbol
. Esto significa que la igualdad de valores y la igualdad de referencia son lo mismo, y por lo tanto, la igualdad es eficiente y O (1).Los motivos para tenerlos en un idioma son realmente los mismos, independientemente del idioma. Algunos idiomas dependen más de ellos que otros.
En la familia Lisp, por ejemplo, no existe el concepto de "variable". En cambio, tiene
Symbol
s asociados a valores.En las lenguas con las capacidades de reflexión o introspección,
Symbol
s se utilizan a menudo para referirse a los nombres de entidades reflejadas en el API de reflexión, por ejemplo, en Rubí,Object#methods
,Object#singleton_methods
,Object#public_methods
,Object#protected_methods
, yObject#public_methods
devolver unaArray
deSymbol
s (aunque podrían igual de bien devolver unaArray
deMethod
s).Object#public_send
toma unSymbol
denotando el nombre del mensaje para enviarlo como argumento (aunque también acepta unString
,Symbol
es más semánticamente correcto).En ECMAScript, los
Symbol
s son un componente fundamental para hacer que ECMAScript sea seguro en el futuro. También juegan un papel importante en la reflexión.fuente
Los símbolos son útiles en Ruby, y los verá en todo el código Ruby porque cada símbolo se reutiliza cada vez que se hace referencia a él. Esta es una mejora del rendimiento sobre las cadenas porque cada uso de una cadena que no se guarda en una variable crea un nuevo objeto en la memoria. Por ejemplo, si uso la misma cadena varias veces como una clave hash:
La cadena "a" se crea 101,000 veces en la memoria. Si usé un símbolo en su lugar:
El símbolo
:a
sigue siendo un objeto en la memoria. Esto hace que los símbolos sean mucho más eficientes que las cadenas.ACTUALIZACIÓN Aquí hay un punto de referencia (tomado de Codecademy ) que demuestra la diferencia de rendimiento:
Aquí están mis resultados para mi MBP:
Hay una clara diferencia en el uso de cadenas frente a símbolos solo para identificar claves en un hash.
fuente
"a"
hecho es una cadena nueva, creo que en su ejemplo habrá exactamente dos"a"
(y una implementación podría incluso compartir la memoria hasta que una de ellas mute). Para crear millones de cadenas, probablemente necesite usar String.new ("a"). Pero no estoy bien versado en Ruby, así que tal vez estoy equivocado.string_AZ[String.new("r")]
fin de ver si eso hace la diferencia. Obtengo 21 ms para cadenas (versión original), 7 ms con símbolos y 50 ms con cadenas nuevas cada vez. Entonces diría que las cadenas no se asignan tanto con la"r"
versión literal .