Uma tupla é uma lista ordenada e finita de elementos, que é imutável. As tuplas exigem que todas as posições tenham um tipo fixo. Isso, por sua vez, significa que o compilador sabe qual tipo está em cada posição. Os tipos usados em uma tupla podem ser diferentes em cada posição, mas os tipos precisam ser conhecidos em tempo de compilação.
Dependendo de se os tipos dos valores da tupla podem ser interpretados em tempo de compilação, a tupla pode ser criada de maneiras diferentes. Se os valores forem conhecidos em tempo de compilação, a tupla pode ser criada usando a sintaxe literal de tupla; caso contrário, eles precisam ser declarados explicitamente. Também é importante que os tipos dos valores correspondam aos tipos especificados na tupla e que o número de valores corresponda ao número de tipos especificados. Veja um exemplo de definição por meio da sintaxe literal de tupla:
tuple = {1, "foo", 'c'} # Tuple(Int32, String, Char)
Também há a possibilidade de criar uma tupla usando a classe Tuple.
tuple = Tuple(Int32, String, Char).new(1, "foo", 'c')
Como alternativa, você pode especificar explicitamente o tipo da variável atribuída à tupla.
tuple : Tuple(Int32, String, Char) = {1, "foo", 'c'}
Especificar explicitamente o tipo da tupla pode ser útil, pois permite definir que uma posição deve conter um tipo de união. Isso significa que uma posição pode conter vários tipos.
tuple : Tuple(Int32 | String, String, Char) = {1, "foo", 'c'}
Você pode criar uma tupla a partir de um array usando o método from da classe Tuple.
Isso exige que o tipo da tupla seja especificado.
array = [1, "foo", 'c']
tuple = Tuple(Int32, String, Char).from(array)
Você pode converter uma tupla em um array usando o método to_a.
O tipo dos elementos do array resultante é a união do tipo de cada campo da tupla.
tuple = {1, "foo", 'c'}
array = tuple.to_a
array # => [1, "foo", 'c']
Assim como os arrays, as tuplas são indexadas a partir de zero, ou seja, o primeiro elemento está no índice 0.
No entanto, diferentemente dos arrays, o tipo de cada elemento é fixo e conhecido em tempo de compilação; portanto, ao indexar uma tupla, o tipo do elemento é específico da posição.
Para acessar um elemento em uma tupla, você pode usar o 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
Outra diferença na hora de acessar elementos de arrays é que, se o índice for especificado, o compilador verificará se o índice está dentro dos limites da tupla. Isso significa que você receberá um erro em tempo de compilação em vez de um erro em tempo de execução.
tuple = {1, "foo", 'c'}
tuple[3]
# => Error: index out of bounds for Tuple(Int32, String, Char) (3 not in -3..2)
Porém, se o índice estiver armazenado em uma variável, o compilador não conseguirá verificar em tempo de compilação se o índice está dentro dos limites da tupla e, em vez disso, dará um erro em tempo de execução.
Você pode obter uma subtupla de uma tupla usando o operador [] com um intervalo.
O que é retornado é uma nova tupla com os elementos do intervalo especificado.
O intervalo precisa ser fornecido em tempo de compilação; caso contrário, o compilador não conseguirá saber os tipos dos elementos da subtupla.
Isso significa que o intervalo precisa ser um literal de intervalo e não pode ser atribuído a uma variável.
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
As tuplas são úteis quando você quer agrupar um número fixo de valores cujos tipos são conhecidos em tempo de compilação. Isso porque as tuplas exigem menos memória e são mais rápidas que os arrays, devido à imutabilidade das tuplas. Outro caso de uso é retornar vários valores de um método. Isso é especialmente útil se os valores tiverem tipos diferentes, já que cada posição da tupla pode ter um tipo diferente.
As tuplas não devem ser usadas quando é necessária uma estrutura de dados que possa crescer ou diminuir de tamanho ou que precise ser modificada com frequência.
Os símbolos são identificadores nomeados que podem ser usados para se referir a um valor.
Os símbolos são criados por meio de um literal de símbolo, que consiste em prefixar um nome com o caractere :, por exemplo :foo.
Eles também podem ser escritos com aspas, por exemplo :"foo", o que permite, por exemplo, espaços no nome.
:foo # => :foo
:"foo boo" # => :"foo boo"
:foo == :"foo" # => true
Os símbolos são usados em muitos lugares da linguagem, inclusive como chaves em namedtuples e para representar nomes de métodos e variáveis.
Os símbolos em Crystal são bem diferentes dos do Ruby. Em Crystal, um símbolo é um tipo de constante e, portanto, é atribuído em tempo de compilação. Isso significa que os símbolos não podem ser criados dinamicamente, o que é possível no Ruby.
Os símbolos em Crystal são representados como Int32s, o que os torna muito eficientes.
Os símbolos podem ser convertidos em strings, mas não o contrário. Isso porque os símbolos são criados em tempo de compilação e as strings em tempo de execução.
:foo.to_s # => "foo"
Enquanto se preparava para assar cookies para seus amigos, você descobriu que precisa converter algumas das medidas usadas na receita. Como só conhece o sistema métrico, você precisa dar um jeito de converter as medidas comuns de culinária dos Estados Unidos para mililitros (mL) para facilitar a sua vida.
Use esta tabela de conversão na sua solução:
| Unidade a converter | volume | em mililitros (mL) |
|---|---|---|
| mL | 1 | 1 |
| xícara americana | 1 | 240 |
| onça líquida americana | 1 | 30 |
| colher de chá americana | 1 | 5 |
| colher de sopa americana | 1 | 15 |
Como um programador talentoso em formação, você decide usar mililitros como unidade de transição para facilitar a conversão de qualquer unidade da lista para qualquer outra (até para ela mesma).
Implemente o método KitchenCalculator.get_volume.
Dada uma tupla de par de volume, ele deve retornar apenas o componente numérico.
KitchenCalculator.get_volume({:cup, 2.0})
# => 2.0
Implemente o método KitchenCalculator.to_milliliter.
Dada uma tupla de par de volume, ele deve converter o volume para mililitros usando a tabela de conversão.
KitchenCalculator.to_milliliter({:cup, 2.5})
# => {:milliliter, 600.0}
Implemente o método KitchenCalculator.from_milliliter.
Dada uma tupla de par de volume e a unidade desejada, ele deve converter o volume para a unidade desejada usando a tabela de conversão.
KitchenCalculator.from_milliliter({:milliliter, 1320.0}, :cup)
# => {:cup, 5.5}
Implemente o método KitchenCalculator.convert.
Dada uma tupla de par de volume e a unidade desejada, ele deve converter o volume dado para a unidade desejada.
KitchenCalculator.convert({:teaspoon, 9.0}, :tablespoon)
# => {:tablespoon, 3.0}
Crie sua conta no Exercism para aprender e dominar Crystal com 26 conceitos142 exercícios e mentoria humana de verdade, tudo de graça.