Las estructuras son una extensión construida sobre los mapas y proporcionan comprobaciones en tiempo de compilación y valores por defecto. Una estructura recibe el nombre del módulo en el que se define. Para definir una estructura, usa el constructor defstruct. El constructor normalmente va justo después de la definición del módulo. defstruct acepta una lista de átomos (para que los valores por defecto sean nil) o una lista de palabras clave (para valores por defecto especificados). Los campos sin valores por defecto deben preceder a los campos con valores por defecto.
defmodule Plane do
defstruct [:engine, wings: 2]
end
plane = %Plane{}
# => %Plane{engine: nil, wings: 2}
Como las estructuras se construyen sobre mapas, podemos usar la mayoría de las funciones de mapas para obtener y manipular valores. El comportamiento Access no está implementado para las estructuras. En su lugar, se recomienda usar el operador de acceso estático . para acceder a los campos de la estructura.
obtener/consultar los valores de los campos:
plane = %Plane{}
plane.engine
# => nil
Map.fetch(plane, :wings)
# => {:ok, 2}
actualizar los valores de los campos
plane = %Plane{}
%{plane | wings: 4}
# => %Plane{engine: nil, wings: 4}
Las estructuras se pueden usar en la coincidencia de patrones con o sin el nombre de la estructura.
plane = %Plane{}
%Plane{wings: wings} = plane
%{wings: wings} = plane
Al incluir el nombre de la estructura en el patrón, puedes asegurarte de que tanto el lado izquierdo como el derecho sean estructuras del mismo 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 el atributo de módulo @enforce_keys con una lista de las claves de los campos para asegurarnos de que los valores se inicialicen cuando se crea la estructura. Si una clave no está en la lista, su valor será nil, como se ve en el ejemplo anterior. Si una clave forzada no se inicializa, se lanza un error.
defmodule User do
@enforce_keys [:username]
defstruct [:username]
end
%User{}
# => (ArgumentError) the following keys must also be given when building struct User: [:username]
En este ejercicio vas a jugar con un coche teledirigido, que por fin has podido comprar después de ahorrar lo suficiente.
Los coches empiezan con la batería llena (100 %). Cada vez que conduces el coche con el mando a distancia, recorre 20 metros y consume un 1 % de la batería. El apodo del coche no se conoce hasta que se crea.
El coche teledirigido tiene una moderna pantalla LED que muestra dos datos:
"<METERS> meters"."Battery at <PERCENTAGE>%".Si la batería está al 0 %, ya no puedes conducir el coche y la pantalla de la batería mostrará "Battery empty".
Implementa la función RemoteControlCar.new/0 para que devuelva un struct de coche teledirigido nuevo:
RemoteControlCar.new()
# => %RemoteControlCar{
# battery_percentage: 100,
# distance_driven_in_meters: 0,
# nickname: "none"
# }
El struct requiere el apodo; asegúrate de inicializar un valor en la función new, pero no en el struct.
Implementa la función RemoteControlCar.new/1 para que devuelva un struct de coche teledirigido nuevo con un apodo:
RemoteControlCar.new("Blue")
# => %RemoteControlCar{
# battery_percentage: 100,
# distance_driven_in_meters: 0,
# nickname: "Blue"
# }
Implementa la función RemoteControlCar.display_distance/1 para que devuelva la distancia tal como se muestra en la pantalla LED:
car = RemoteControlCar.new()
RemoteControlCar.display_distance(car)
# => "0 meters"
Asegúrate de que la función solo acepte un struct RemoteControlCar como argumento.
Implementa la función RemoteControlCar.display_battery/1 para que devuelva el porcentaje de batería tal como se muestra en la pantalla LED:
car = RemoteControlCar.new()
RemoteControlCar.display_battery(car)
# => "Battery at 100%"
Asegúrate de que la función solo acepte un struct RemoteControlCar como argumento. Si la batería está al 0 %, la pantalla de la batería mostrará «Battery empty».
Implementa la función RemoteControlCar.drive/1 que:
RemoteControlCar.new("Red")
|> RemoteControlCar.drive()
# => %RemoteControlCar{
# battery_percentage: 99,
# distance_driven_in_meters: 20,
# nickname: "Red"
# }
Asegúrate de que la función solo acepte un struct RemoteControlCar como argumento.
Actualiza la función RemoteControlCar.drive/1 para que no aumente la distancia recorrida ni reduzca el porcentaje de batería cuando la batería está agotada (al 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"
# }
Regístrate en Exercism para aprender y dominar Elixir con 58 conceptos168 ejercicios y mentoría humana real, todo gratis.