Una tupla es una lista ordenada y 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.
Según si los tipos de los valores de la tupla se pueden interpretar durante la compilación, la tupla se puede crear de distintas maneras. Si los valores se conocen en tiempo de compilación, la tupla se puede crear con la sintaxis de 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 la cantidad de valores coincida con la cantidad de tipos especificados. Aquí tienes un ejemplo de definición mediante la sintaxis de 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 a la que se asigna 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 de 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.
Para esto hay que especificar 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 los elementos 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 empiezan en el índice 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 al acceder a los elementos de un array es que, si el índice está especificado, el compilador verificará 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 está guardado en una variable, el compilador no podrá verificar en tiempo de compilación si el índice está dentro de los límites de la tupla 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 indicarse en tiempo de compilación; de lo contrario, el compilador no podrá saber los tipos de los elementos de la subtupla.
Esto significa que el rango debe 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 una cantidad fija 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 gracias a su inmutabilidad. Otro caso de uso es devolver varios valores desde un método. Esto resulta especialmente útil si los valores tienen tipos distintos, ya que cada posición de la tupla puede tener un tipo diferente.
No conviene usar tuplas cuando se necesita una estructura de datos que pueda crecer o reducir su tamaño, o que deba 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 anteponer un carácter : a un nombre, por ejemplo :foo.
También se pueden escribir entre comillas, por ejemplo :"foo", lo que permite, por ejemplo, incluir 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 namedtuples 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 de forma dinámica, algo que sí es posible en Ruby.
Los símbolos en Crystal se representan como Int32, 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 preparabas para hornear galletas para tus amigos, descubriste que tienes que convertir algunas de las medidas que usa la receta. Como solo conoces el sistema métrico, necesitas encontrar una forma de convertir las medidas de repostería estadounidenses más comunes 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 tienes talento para programar y estás 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 par 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 par 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 par 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 par 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.