Structs sind eine Erweiterung, die auf Maps aufbaut und Prüfungen zur Kompilierzeit sowie Standardwerte bietet. Ein Struct ist nach dem Modul benannt, in dem es definiert wird. Um ein Struct zu definieren, verwendest du das Konstrukt defstruct. Das Konstrukt folgt normalerweise direkt auf die Moduldefinition. defstruct akzeptiert entweder eine Liste von Atomen (für Standardwerte nil) oder eine Keyword-Liste (für festgelegte Standardwerte). Die Felder ohne Standardwerte müssen vor den Feldern mit Standardwerten stehen.
defmodule Plane do
defstruct [:engine, wings: 2]
end
plane = %Plane{}
# => %Plane{engine: nil, wings: 2}
Da Structs auf Maps aufbauen, können wir die meisten Map-Funktionen verwenden, um Werte abzurufen und zu verändern. Das Access Behaviour ist für Structs nicht implementiert. Stattdessen empfiehlt es sich, den statischen Zugriffsoperator . zu verwenden, um auf Struct-Felder zuzugreifen.
Feldwerte abrufen:
plane = %Plane{}
plane.engine
# => nil
Map.fetch(plane, :wings)
# => {:ok, 2}
Feldwerte aktualisieren
plane = %Plane{}
%{plane | wings: 4}
# => %Plane{engine: nil, wings: 4}
Structs können im Pattern Matching mit oder ohne den Struct-Namen verwendet werden.
plane = %Plane{}
%Plane{wings: wings} = plane
%{wings: wings} = plane
Wenn du den Struct-Namen im Muster angibst, stellst du sicher, dass sowohl die linke als auch die rechte Seite Structs desselben Typs sind.
defmodule Helicopter do
defstruct [:engine, rotors: 1]
end
%Plane{} = %Helicopter{}
# => (MatchError) no match of right hand side value: %Helicopter{engine: nil, rotors: 1}
Wir können das Modulattribut @enforce_keys mit einer Liste der Feldschlüssel verwenden, um sicherzustellen, dass die Werte initialisiert werden, wenn das Struct erstellt wird. Wenn ein Schlüssel nicht aufgeführt ist, ist sein Wert nil, wie im obigen Beispiel zu sehen. Wenn ein erzwungener Schlüssel nicht initialisiert wird, wird ein Fehler ausgelöst.
defmodule User do
@enforce_keys [:username]
defstruct [:username]
end
%User{}
# => (ArgumentError) the following keys must also be given when building struct User: [:username]
In dieser Übung beschäftigst du dich mit einem ferngesteuerten Auto, für das du endlich genug Geld gespart hast.
Autos starten mit vollen (100 %) Akkus. Jedes Mal, wenn du das Auto mit der Fernbedienung fährst, legt es 20 Meter zurück und verbraucht ein Prozent des Akkus. Der Spitzname des Autos ist erst bekannt, wenn das Auto erstellt wird.
Das ferngesteuerte Auto hat ein schickes LED-Display, das zwei Informationen anzeigt:
"<METERS> meters"."Battery at <PERCENTAGE>%".Wenn der Akku bei 0 % ist, kannst du das Auto nicht mehr fahren und die Akkuanzeige zeigt "Battery empty" an.
Implementiere die Funktion RemoteControlCar.new/0, die ein brandneues ferngesteuertes Auto-Struct zurückgibt:
RemoteControlCar.new()
# => %RemoteControlCar{
# battery_percentage: 100,
# distance_driven_in_meters: 0,
# nickname: "none"
# }
Der Spitzname ist im Struct erforderlich. Stelle sicher, dass ein Wert in der Funktion new initialisiert wird, aber nicht im Struct.
Implementiere die Funktion RemoteControlCar.new/1, die ein brandneues ferngesteuertes Auto-Struct mit einem übergebenen Spitznamen zurückgibt:
RemoteControlCar.new("Blue")
# => %RemoteControlCar{
# battery_percentage: 100,
# distance_driven_in_meters: 0,
# nickname: "Blue"
# }
Implementiere die Funktion RemoteControlCar.display_distance/1, die die Strecke so zurückgibt, wie sie auf dem LED-Display angezeigt wird:
car = RemoteControlCar.new()
RemoteControlCar.display_distance(car)
# => "0 meters"
Stelle sicher, dass die Funktion nur ein RemoteControlCar-Struct als Argument akzeptiert.
Implementiere die Funktion RemoteControlCar.display_battery/1, die den Akkustand so zurückgibt, wie er auf dem LED-Display angezeigt wird:
car = RemoteControlCar.new()
RemoteControlCar.display_battery(car)
# => "Battery at 100%"
Stelle sicher, dass die Funktion nur ein RemoteControlCar-Struct als Argument akzeptiert. Wenn der Akku bei 0 % ist, zeigt die Akkuanzeige "Battery empty" an.
Implementiere die Funktion RemoteControlCar.drive/1, die:
RemoteControlCar.new("Red")
|> RemoteControlCar.drive()
# => %RemoteControlCar{
# battery_percentage: 99,
# distance_driven_in_meters: 20,
# nickname: "Red"
# }
Stelle sicher, dass die Funktion nur ein RemoteControlCar-Struct als Argument akzeptiert.
Aktualisiere die Funktion RemoteControlCar.drive/1, sodass sie die gefahrene Strecke nicht erhöht und den Akkustand nicht verringert, wenn der Akku leer ist (bei 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"
# }
Melde dich bei Exercism an, um Elixir mit 58 Konzepte168 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.