Gli struct sono un'estensione costruita sopra le mappe e forniscono controlli in fase di compilazione e valori predefiniti. Uno struct prende il nome dal modulo in cui è definito. Per definire uno struct si usa il costrutto defstruct. Di solito questo costrutto segue subito la definizione del modulo. defstruct accetta sia un elenco di atomi (per valori predefiniti nil) sia una lista di parole chiave (per valori predefiniti specificati). I campi senza valore predefinito devono precedere i campi con un valore predefinito.
defmodule Plane do
defstruct [:engine, wings: 2]
end
plane = %Plane{}
# => %Plane{engine: nil, wings: 2}
Dato che gli struct sono costruiti sulle mappe, possiamo usare gran parte delle funzioni per le mappe per leggere e modificare i valori. Il Access Behaviour non è implementato per gli struct. Per accedere ai campi di uno struct, si consiglia invece di usare l'operatore di accesso statico ..
per leggere o recuperare i valori dei campi:
plane = %Plane{}
plane.engine
# => nil
Map.fetch(plane, :wings)
# => {:ok, 2}
per aggiornare i valori dei campi
plane = %Plane{}
%{plane | wings: 4}
# => %Plane{engine: nil, wings: 4}
Gli struct si possono usare nel pattern matching con o senza il nome dello struct.
plane = %Plane{}
%Plane{wings: wings} = plane
%{wings: wings} = plane
Includendo il nome dello struct nel pattern, puoi assicurarti che sia il lato sinistro sia quello destro siano struct dello stesso tipo.
defmodule Helicopter do
defstruct [:engine, rotors: 1]
end
%Plane{} = %Helicopter{}
# => (MatchError) no match of right hand side value: %Helicopter{engine: nil, rotors: 1}
Possiamo usare l'attributo di modulo @enforce_keys con un elenco delle chiavi dei campi per assicurarci che i valori siano inizializzati al momento della creazione dello struct. Se una chiave non compare nell'elenco, il suo valore sarà nil, come si vede nell'esempio qui sopra. Se una chiave obbligatoria non viene inizializzata, viene generato un errore.
defmodule User do
@enforce_keys [:username]
defstruct [:username]
end
%User{}
# => (ArgumentError) the following keys must also be given when building struct User: [:username]
In questo esercizio giocherai con un'auto radiocomandata, per cui hai finalmente risparmiato abbastanza soldi da poterla comprare.
Le auto partono con la batteria completamente carica (100%). Ogni volta che guidi l'auto con il radiocomando, percorre 20 metri e consuma l'uno per cento della batteria. Il soprannome dell'auto non è noto finché non viene creata.
L'auto radiocomandata ha un elegante display a LED che mostra due informazioni:
"<METERS> meters"."Battery at <PERCENTAGE>%".Se la batteria è allo 0%, non puoi più guidare l'auto e il display della batteria mostrerà "Battery empty".
Implementa la funzione RemoteControlCar.new/0 in modo che restituisca una struct di un'auto radiocomandata nuova di zecca:
RemoteControlCar.new()
# => %RemoteControlCar{
# battery_percentage: 100,
# distance_driven_in_meters: 0,
# nickname: "none"
# }
Il soprannome è richiesto dalla struct: assicurati che un valore venga inizializzato nella funzione new, ma non nella struct.
Implementa la funzione RemoteControlCar.new/1 in modo che restituisca una struct di un'auto radiocomandata nuova di zecca con un soprannome fornito:
RemoteControlCar.new("Blue")
# => %RemoteControlCar{
# battery_percentage: 100,
# distance_driven_in_meters: 0,
# nickname: "Blue"
# }
Implementa la funzione RemoteControlCar.display_distance/1 in modo che restituisca la distanza così come viene visualizzata sul display a LED:
car = RemoteControlCar.new()
RemoteControlCar.display_distance(car)
# => "0 meters"
Assicurati che la funzione accetti come argomento solo una struct RemoteControlCar.
Implementa la funzione RemoteControlCar.display_battery/1 in modo che restituisca la percentuale della batteria così come viene visualizzata sul display a LED:
car = RemoteControlCar.new()
RemoteControlCar.display_battery(car)
# => "Battery at 100%"
Assicurati che la funzione accetti come argomento solo una struct RemoteControlCar. Se la batteria è allo 0%, il display della batteria mostrerà "Battery empty".
Implementa la funzione RemoteControlCar.drive/1 che:
RemoteControlCar.new("Red")
|> RemoteControlCar.drive()
# => %RemoteControlCar{
# battery_percentage: 99,
# distance_driven_in_meters: 20,
# nickname: "Red"
# }
Assicurati che la funzione accetti come argomento solo una struct RemoteControlCar.
Aggiorna la funzione RemoteControlCar.drive/1 in modo che non aumenti la distanza percorsa né diminuisca la percentuale della batteria quando la batteria è scarica (allo 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"
# }
Iscriviti a Exercism per imparare e padroneggiare Elixir con 58 concetti168 esercizi e il mentoring di persone reali, tutto gratis.