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}
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}
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}
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]
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:
"<METERS> meters"."Battery at <PERCENTAGE>%".Se a bateria estiver a 0%, já não podes conduzir o carro e o ecrã da bateria mostra "Battery empty".
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.
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"
# }
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.
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".
Implementa a função RemoteControlCar.drive/1 que:
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.
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"
# }
Inscreve-te no Exercism para aprenderes e dominares Elixir com 58 conceitos168 exercícios, e mentoria humana real, tudo grátis.