Percursos
/
Crystal
Crystal
/
Exercícios
/
Calculadora de Cozinha
Calculadora de Cozinha

Calculadora de Cozinha

Exercício de aprendizagem

Introdução

Um tuplo é uma lista finita e ordenada de elementos, e é imutável. Os tuplos exigem que todas as posições tenham um tipo fixo. Isto significa, por sua vez, que o compilador sabe que tipo está em cada posição. Os tipos usados num tuplo podem ser diferentes em cada posição, mas têm de ser conhecidos em tempo de compilação.

Criar um tuplo

Dependendo de se os tipos dos valores do tuplo podem ser interpretados em tempo de compilação, o tuplo pode ser criado de formas diferentes. Se os valores forem conhecidos em tempo de compilação, o tuplo pode ser criado com a sintaxe de literal de tuplo; caso contrário, têm de ser declarados explicitamente. Também é importante que os tipos dos valores correspondam aos tipos especificados no tuplo e que o número de valores corresponda ao número de tipos indicados. Eis um exemplo de definição através da sintaxe de literal de tuplo:

tuple = {1, "foo", 'c'} # Tuple(Int32, String, Char)

Também é possível criar um tuplo com a classe Tuple.

tuple = Tuple(Int32, String, Char).new(1, "foo", 'c')

Em alternativa, podes especificar explicitamente o tipo da variável atribuída ao tuplo.

tuple : Tuple(Int32, String, Char) = {1, "foo", 'c'}

Especificar explicitamente o tipo do tuplo pode ser útil, pois permite definir que uma posição deve conter um tipo união. Isto significa que uma posição pode conter vários tipos.

tuple : Tuple(Int32 | String, String, Char) = {1, "foo", 'c'}

Conversão

Criar um tuplo a partir de um array

Podes criar um tuplo a partir de um array com o método from da classe Tuple. Isto exige que o tipo do tuplo seja especificado.

array = [1, "foo", 'c']
tuple = Tuple(Int32, String, Char).from(array)

Conversão para array

Podes converter um tuplo num array com o método to_a. O tipo dos elementos do array resultante é a união dos tipos de cada campo do tuplo.

tuple = {1, "foo", 'c'}
array = tuple.to_a
array # => [1, "foo", 'c']

Aceder a elementos

Tal como os arrays, os tuplos são indexados a partir de zero, ou seja, o primeiro elemento está no índice 0. No entanto, ao contrário dos arrays, o tipo de cada elemento é fixo e conhecido em tempo de compilação; por isso, ao indexar um tuplo, o tipo do elemento é específico da posição. Para aceder a um elemento de um tuplo, podes 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 em relação ao acesso a elementos de arrays é que, se o índice for especificado, o compilador verifica se o índice está dentro dos limites do tuplo. Isto significa que recebes 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)

No entanto, se o índice estiver guardado numa variável, o compilador não consegue verificar se o índice está dentro dos limites do tuplo em tempo de compilação e dá, em vez disso, um erro em tempo de execução.

Subtuplo

Podes obter um subtuplo de um tuplo usando o operador [] com um intervalo. O que é devolvido é um novo tuplo com os elementos do intervalo indicado. O intervalo tem de ser indicado em tempo de compilação; caso contrário, o compilador não consegue saber os tipos dos elementos do subtuplo. Isto significa que o intervalo tem de ser um literal de intervalo e não pode estar 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

Quando usar um tuplo

Os tuplos são úteis quando queres agrupar um número fixo de valores cujos tipos são conhecidos em tempo de compilação. Isto porque os tuplos ocupam menos memória e são mais rápidos do que os arrays, graças à sua imutabilidade. Outro caso de uso é devolver vários valores a partir de um método. Isto é particularmente útil se os valores tiverem tipos diferentes, uma vez que cada posição do tuplo pode ter um tipo diferente.

Não deves usar tuplos quando precisas de uma estrutura de dados que possa crescer ou encolher de tamanho ou que tenha de ser modificada com frequência.

Os símbolos são identificadores com nome que podem ser usados para referir um valor. Os símbolos são criados através de um literal de símbolo, que consiste em colocar o caráter : antes de um nome, por exemplo, :foo. 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 sítios da linguagem, incluindo como chaves em namedtuples e para representar nomes de métodos e variáveis.

Símbolos em Crystal

Os símbolos em Crystal são bastante diferentes dos de Ruby. Em Crystal, um símbolo é um tipo de constante e, por isso, é atribuído em tempo de compilação. Isto significa que os símbolos não podem ser criados dinamicamente, algo que é possível em Ruby.

Em Crystal, os símbolos são representados como Int32s, o que os torna muito eficientes.

Conversão

Os símbolos podem ser convertidos em strings, mas não o contrário. Isto porque os símbolos são criados em tempo de compilação e as strings são criadas em tempo de execução.

:foo.to_s # => "foo"

Instruções

Enquanto te preparavas para fazer bolachas para os teus amigos, descobriste que tens de converter algumas das medidas usadas na receita. Como só conheces o sistema métrico, precisas de arranjar uma forma de converter as medidas de culinária mais comuns dos Estados Unidos em mililitros (mL), para te facilitar a vida.

Usa esta tabela de conversão na tua solução:

Unidade a converter volume em mililitros (mL)
mL 1 1
chávena americana 1 240
onça líquida americana 1 30
colher de chá americana 1 5
colher de sopa americana 1 15

Como tens talento para a programação e estás em formação, decides usar os mililitros como unidade de transição, para facilitar a conversão de qualquer unidade da lista para qualquer outra (mesmo para ela própria).

1. Obter a componente numérica de um par de volume

Implementa o método KitchenCalculator.get_volume. Dado um par de volume, na forma de tuplo, deve devolver apenas a componente numérica.

KitchenCalculator.get_volume({:cup, 2.0})
# => 2.0

2. Converter o par de volume em mililitros

Implementa o método KitchenCalculator.to_milliliter. Dado um par de volume, na forma de tuplo, deve converter o volume em mililitros, usando a tabela de conversão.

KitchenCalculator.to_milliliter({:cup, 2.5})
# => {:milliliter, 600.0}

3. Converter o par de volume em mililitros para outra unidade

Implementa o método KitchenCalculator.from_milliliter. Dado um par de volume, na forma de tuplo, e a unidade pretendida, deve converter o volume para a unidade pretendida, usando a tabela de conversão.

KitchenCalculator.from_milliliter({:milliliter, 1320.0}, :cup)
# => {:cup, 5.5}

4. Converter de qualquer unidade para qualquer unidade

Implementa o método KitchenCalculator.convert. Dado um par de volume, na forma de tuplo, e a unidade pretendida, deve converter o volume indicado para a unidade pretendida.

KitchenCalculator.convert({:teaspoon, 9.0}, :tablespoon)
# => {:tablespoon, 3.0}
Editar via GitHub A ligação abre numa nova janela ou separador
Crystal Exercism

Estás pronto para começar Calculadora de Cozinha?

Inscreve-te no Exercism para aprenderes e dominares Crystal com 26 conceitos142 exercícios, e mentoria humana real, tudo grátis.