Una tupla es una lista ordenada finita de elementos que es inmutable. Las tuplas requieren que todas las posiciones tengan un tipo fijo. Esto, a su vez, significa que el compilador sabe qué tipo hay en cada posición. Los tipos usados en una tupla pueden ser diferentes en cada posición, pero los tipos deben conocerse en tiempo de compilación.
Dependiendo de si los tipos de los valores de la tupla se pueden interpretar durante la compilación, la tupla se puede crear de diferentes formas. Si los valores se conocen en tiempo de compilación, la tupla se puede crear usando la sintaxis literal de tupla; de lo contrario, hay que declararlos explícitamente. También es importante que los tipos de los valores coincidan con los tipos especificados en la tupla y que el número de valores coincida con el número de tipos especificados. Aquí tienes un ejemplo de definición mediante la sintaxis literal de tupla:
tuple = {1, "foo", 'c'} # Tuple(Int32, String, Char)
También existe la posibilidad de crear una tupla usando la clase Tuple.
tuple = Tuple(Int32, String, Char).new(1, "foo", 'c')
Como alternativa, puedes especificar explícitamente el tipo de la variable asignada a la tupla.
tuple : Tuple(Int32, String, Char) = {1, "foo", 'c'}
Especificar explícitamente el tipo de la tupla puede ser útil, ya que permite definir que una posición contenga un tipo unión. Esto significa que una posición puede contener varios tipos.
tuple : Tuple(Int32 | String, String, Char) = {1, "foo", 'c'}
Puedes crear una tupla a partir de un array usando el método from de la clase Tuple.
Esto requiere que se especifique el tipo de la tupla.
array = [1, "foo", 'c']
tuple = Tuple(Int32, String, Char).from(array)
Puedes convertir una tupla a un array usando el método to_a.
El tipo de elemento del array resultante es la unión del tipo de cada campo de la tupla.
tuple = {1, "foo", 'c'}
array = tuple.to_a
array # => [1, "foo", 'c']
Al igual que los arrays, las tuplas están indexadas desde cero, lo que significa que el primer elemento está en el índice 0.
Sin embargo, a diferencia de los arrays, el tipo de cada elemento es fijo y se conoce en tiempo de compilación; por lo tanto, al indexar una tupla, el tipo del elemento es específico de la posición.
Para acceder a un elemento de una tupla, puedes usar el operador [].
array = [1, "foo", 'c']
array[0] # => 1
typeof(array[0]) # => Int32 | String | Char
tuple = {1, "foo", 'c'}
tuple[0] # => 1
typeof(tuple[0]) # => Int32
Otra diferencia a la hora de acceder a los elementos de los arrays es que, si se especifica el índice, el compilador comprobará que el índice esté dentro de los límites de la tupla. Esto significa que obtendrás un error en tiempo de compilación en lugar de un error en tiempo de ejecución.
tuple = {1, "foo", 'c'}
tuple[3]
# => Error: index out of bounds for Tuple(Int32, String, Char) (3 not in -3..2)
Sin embargo, si el índice se guarda en una variable, el compilador no podrá comprobar si el índice está dentro de los límites de la tupla en tiempo de compilación y, en su lugar, dará un error en tiempo de ejecución.
Puedes obtener una subtupla de una tupla usando el operador [] con un rango.
Lo que se devuelve es una nueva tupla con los elementos del rango especificado.
El rango debe darse en tiempo de compilación; de lo contrario, el compilador no podrá conocer los tipos de los elementos de la subtupla.
Esto significa que el rango tiene que ser un literal de rango y no puede estar asignado a una variable.
tuple = {1, "foo", 'c'}
subtuple = tuple[0..1] # Tuple(Int32, String)
i = 0..1
tuple[i]
# Error: Tuple#[](Range) can only be called with range literals known at compile-time
Las tuplas son útiles cuando quieres agrupar un número fijo de valores cuyos tipos se conocen en tiempo de compilación. Esto se debe a que las tuplas requieren menos memoria y son más rápidas que los arrays debido a la inmutabilidad de las tuplas. Otro caso de uso es devolver varios valores desde un método. Esto es especialmente útil si los valores tienen tipos diferentes, ya que cada posición de la tupla puede tener un tipo diferente.
Las tuplas no deben usarse cuando se necesita una estructura de datos que pueda crecer o reducir su tamaño, o que necesite modificarse con frecuencia.
Los símbolos son identificadores con nombre que se pueden usar para referirse a un valor.
Los símbolos se crean mediante un literal de símbolo, que consiste en prefijar un nombre con el carácter :, p. ej. :foo.
También se pueden escribir entre comillas, p. ej. :"foo", lo que permite, por ejemplo, espacios en el nombre.
:foo # => :foo
:"foo boo" # => :"foo boo"
:foo == :"foo" # => true
Los símbolos se usan en muchos lugares del lenguaje, incluso como claves en tuplas con nombre y para representar nombres de métodos y variables.
Los símbolos en Crystal son bastante diferentes de los de Ruby. En Crystal, un símbolo es un tipo de constante y, por lo tanto, se asigna en tiempo de compilación. Esto significa que los símbolos no se pueden crear dinámicamente, algo que sí es posible en Ruby.
Los símbolos en Crystal se representan como Int32s, lo que los hace muy eficientes.
Los símbolos se pueden convertir a strings, pero no al revés. Esto se debe a que los símbolos se crean en tiempo de compilación y los strings se crean en tiempo de ejecución.
:foo.to_s # => "foo"
Mientras te preparas para hornear galletas para tus amigos, te has dado cuenta de que tienes que convertir algunas de las medidas que usa la receta. Como solo conoces el sistema métrico, necesitas encontrar la forma de convertir las medidas de repostería estadounidenses más habituales a mililitros (mL) para tu propia comodidad.
Usa esta tabla de conversión para tu solución:
| Unidad a convertir | volumen | en mililitros (mL) |
|---|---|---|
| mL | 1 | 1 |
| taza estadounidense | 1 | 240 |
| onza líquida estadounidense | 1 | 30 |
| cucharadita estadounidense | 1 | 5 |
| cucharada estadounidense | 1 | 15 |
Como eres un programador talentoso en formación, decides usar los mililitros como unidad de transición para facilitar la conversión de cualquier unidad de la lista a cualquier otra (incluso a sí misma).
Implementa el método KitchenCalculator.get_volume.
Dada una tupla de volumen, debe devolver solo el componente numérico.
KitchenCalculator.get_volume({:cup, 2.0})
# => 2.0
Implementa el método KitchenCalculator.to_milliliter.
Dada una tupla de volumen, debe convertir el volumen a mililitros usando la tabla de conversión.
KitchenCalculator.to_milliliter({:cup, 2.5})
# => {:milliliter, 600.0}
Implementa el método KitchenCalculator.from_milliliter.
Dada una tupla de volumen y la unidad deseada, debe convertir el volumen a la unidad deseada usando la tabla de conversión.
KitchenCalculator.from_milliliter({:milliliter, 1320.0}, :cup)
# => {:cup, 5.5}
Implementa el método KitchenCalculator.convert.
Dada una tupla de volumen y la unidad deseada, debe convertir el volumen dado a la unidad deseada.
KitchenCalculator.convert({:teaspoon, 9.0}, :tablespoon)
# => {:tablespoon, 3.0}
Regístrate en Exercism para aprender y dominar Crystal con 26 conceptos142 ejercicios y mentoría humana real, todo gratis.