Percursos
/
Elixir
Elixir
/
Exercícios
/
Carro telecomandado
Carro telecomandado

Carro telecomandado

Exercício de aprendizagem

Introdução

Structs

Os structs são uma extensão construída sobre os mapas que oferece verificações em tempo de compilação e valores predefinidos. Um struct recebe o nome do módulo em que é definido. Para definir um struct, usa o construto defstruct. O construto aparece normalmente logo a seguir à definição do módulo. O defstruct aceita uma lista de átomos (para valores predefinidos nil) ou uma lista de palavras-chave (para valores predefinidos especificados). Os campos sem valor predefinido têm de vir antes dos campos com valores predefinidos.

defmodule Plane do
  defstruct [:engine, wings: 2]
end

plane = %Plane{}
# => %Plane{engine: nil, wings: 2}

Acesso e atualização dos campos

Como os structs são construídos sobre mapas, podemos usar a maioria das funções de mapas para obter e manipular valores. O Access Behaviour não está implementado para structs. Em vez disso, recomenda-se o uso do operador de acesso estático . para aceder aos campos de um struct.

  • obter/recuperar valores dos campos:

    plane = %Plane{}
    plane.engine
    # => nil
    Map.fetch(plane, :wings)
    # => {:ok, 2}
    
  • atualizar valores dos campos

    plane = %Plane{}
    %{plane | wings: 4}
    # => %Plane{engine: nil, wings: 4}
    

Correspondência de padrões

Os structs podem ser usados em correspondência de padrões com ou sem o nome do struct.

plane = %Plane{}
%Plane{wings: wings} = plane
%{wings: wings} = plane

Ao incluir o nome do struct no padrão, garantes que tanto o lado esquerdo como o direito são structs do mesmo tipo.

defmodule Helicopter do
  defstruct [:engine, rotors: 1]
end

%Plane{} = %Helicopter{}
# => (MatchError) no match of right hand side value: %Helicopter{engine: nil, rotors: 1}

Forçar a inicialização dos valores dos campos

Podemos usar o atributo de módulo @enforce_keys com uma lista das chaves dos campos para garantir que os valores são inicializados quando o struct é criado. Se uma chave não estiver na lista, o seu valor será nil, como se vê no exemplo acima. Se uma chave forçada não for inicializada, é lançado um erro.

defmodule User do
  @enforce_keys [:username]
  defstruct [:username]
end

%User{}
# => (ArgumentError) the following keys must also be given when building struct User: [:username]

Instruções

Neste exercício vais brincar com um carro telecomandado, para o qual finalmente poupaste dinheiro suficiente.

Os carros começam com a bateria cheia (100%). Cada vez que conduzes o carro com o telecomando, este percorre 20 metros e gasta um por cento da bateria. A alcunha do carro só é conhecida depois de o carro ser criado.

O carro telecomandado tem um ecrã LED sofisticado que mostra duas informações:

  • A distância total que já percorreu, apresentada como: "<METERS> meters".
  • A carga restante da bateria, apresentada como: "Battery at <PERCENTAGE>%".

Se a bateria estiver a 0%, já não podes conduzir o carro e o ecrã da bateria mostra "Battery empty".

1. Cria um carro telecomandado novo em folha

Implementa a função RemoteControlCar.new/0 para devolver uma struct de carro telecomandado novo em folha:

RemoteControlCar.new()
# => %RemoteControlCar{
#      battery_percentage: 100,
#      distance_driven_in_meters: 0,
#      nickname: "none"
#    }

A alcunha é exigida pela struct; certifica-te de que inicializas um valor na função new, mas não na struct.

2. Cria um carro telecomandado novo em folha com uma alcunha

Implementa a função RemoteControlCar.new/1 para devolver uma struct de carro telecomandado novo em folha com uma alcunha fornecida:

RemoteControlCar.new("Blue")
# => %RemoteControlCar{
#      battery_percentage: 100,
#      distance_driven_in_meters: 0,
#      nickname: "Blue"
#    }

3. Mostra a distância

Implementa a função RemoteControlCar.display_distance/1 para devolver a distância tal como é apresentada no ecrã LED:

car = RemoteControlCar.new()
RemoteControlCar.display_distance(car)
# => "0 meters"

Certifica-te de que a função só aceita uma struct RemoteControlCar como argumento.

4. Mostra a percentagem da bateria

Implementa a função RemoteControlCar.display_battery/1 para devolver a percentagem da bateria tal como é apresentada no ecrã LED:

car = RemoteControlCar.new()
RemoteControlCar.display_battery(car)
# => "Battery at 100%"

Certifica-te de que a função só aceita uma struct RemoteControlCar como argumento. Se a bateria estiver a 0%, o ecrã da bateria mostra "Battery empty".

5. Conduzir altera a bateria e a distância percorrida

Implementa a função RemoteControlCar.drive/1 que:

  • aumenta em 20 o número de metros percorridos
  • gasta 1% da bateria
RemoteControlCar.new("Red")
|> RemoteControlCar.drive()

# => %RemoteControlCar{
#      battery_percentage: 99,
#      distance_driven_in_meters: 20,
#      nickname: "Red"
#    }

Certifica-te de que a função só aceita uma struct RemoteControlCar como argumento.

6. Tem em conta a condução com a bateria descarregada

Atualiza a função RemoteControlCar.drive/1 para não aumentar a distância percorrida nem diminuir a percentagem da bateria quando a bateria está gasta (a 0%):

%RemoteControlCar{
  battery_percentage: 0,
  distance_driven_in_meters: 1980,
  nickname: "Red"
}
|> RemoteControlCar.drive()

# => %RemoteControlCar{
#      battery_percentage: 0,
#      distance_driven_in_meters: 1980,
#      nickname: "Red"
#    }
Editar via GitHub A ligação abre numa nova janela ou separador
Elixir Exercism

Estás pronto para começar Carro telecomandado?

Inscreve-te no Exercism para aprenderes e dominares Elixir com 58 conceitos168 exercícios, e mentoria humana real, tudo grátis.