Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Beginner 5

src/livebook/beginner5.livemd

Beginner 5

Forberedelse

Et map er en datastruktur der associerer hvert element fra én mængde af værdier med netop én element i en anden mængde af værdier. Den første mængde er vores nøgler (en: keys) og den anden er disse nøglers værdier (en: values).

Arbitrære nøgler

Lad os prøve at bruge et map til at repræsentere forholdet mellem forkortelser på måneder og disse måneders længder:

leap_year = false
month_lengths = %{
  "jan" => 31,
  "feb" => 28 + if leap_year do 1 else 0 end,
  "mar" => 31,
  "apr" => 30,
  "maj" => 31,
  "jun" => 30,
  "jul" => 31,
  "aug" => 31,
  "sep" => 30,
  "okt" => 31,
  "nov" => 30,
  "dec" => 31,
}

Vi kan nu med Map.get funktionen slå en vilkårlig måneds længde op:

"Oktober er #{Map.get(month_lengths, "okt")} dage lang"

En variant af denne funktion kan gives en default værdi som kommer i spil hvis nøglen ikke findes:

"October er #{Map.get(month_lengths, "oct", "")} dage lang"

Vi kan tilføje en association:

month_lengths = Map.put(month_lengths, "oct", 31)
"October er #{Map.get(month_lengths, "oct", "")} dage lang"

Vi kan spørge om en nøgle eksisterer i vores map:

Map.has_key?(month_lengths, "oct")

I ovenstående har vi brugt strene som keys, men det kunne være hvad som helst. Det kunne eksempelvis være tuples:

month_lengths = %{
  {:da, "jan"} => 31,
  {:da, "feb"} => 28 + if leap_year do 1 else 0 end,
  {:da, "mar"} => 31,
  {:da, "apr"} => 30,
  {:da, "maj"} => 31,
  {:da, "jun"} => 30,
  {:da, "jul"} => 31,
  {:da, "aug"} => 31,
  {:da, "sep"} => 30,
  {:da, "okt"} => 31,
  {:da, "nov"} => 30,
  {:da, "dec"} => 31,
  {:en, "jan"} => 31,
  {:en, "feb"} => 28 + if leap_year do 1 else 0 end,
  {:en, "mar"} => 31,
  {:en, "apr"} => 30,
  {:en, "may"} => 31,
  {:en, "jun"} => 30,
  {:en, "jul"} => 31,
  {:en, "aug"} => 31,
  {:en, "sep"} => 30,
  {:en, "oct"} => 31,
  {:en, "nov"} => 30,
  {:en, "dec"} => 31,
}

Bemærk: Det kunne faktisk være en kombination.

Atomer som nøgler

Et ofte anvendt specialtilfælde er når alle nøglerne er atomer:

month_lengths = %{
  jan: 31,
  feb: 28 + if leap_year do 1 else 0 end,
  mar: 31,
  apr: 30,
  maj: 31,
  jun: 30,
  jul: 31,
  aug: 31,
  sep: 30,
  okt: 31,
  nov: 30,
  dec: 31,
}

Da kan vi tilgå de enkelte nøglers værdier med . operatoren:

month_lengths.okt

Til og fra lister

Maps kan håndteres som lister:

month_lengths
|> Enum.map(fn {key, value} -> "#{key} har værdien #{value}" end)

Her er hvert element der håndteres af den anonyme funtion en tuple, og det pattern matches der på.

Det kan vi udnytte til at gå den anden vej:

l = [
  {"jan", 31},
  {"feb", 28 + if leap_year do 1 else 0 end},
  {"mar", 31},
  {"apr", 30},
  {"maj", 31},
  {"jun", 30},
  {"jul", 31},
  {"aug", 31},
  {"sep", 30},
  {"okt", 31},
  {"nov", 30},
  {"dec", 31},
]
Map.new(l)

Øvelse

Lad os tage et helt tilfældigt år som udgangspunkt:

year = 2024

Reglerne om skudår siger, at

“This extra leap day occurs in each year that is a multiple of 4, except for years evenly divisible by 100 but not by 400”

Lav nu en erklæring af variablen leap_year? der afhænger af værdien af variablen year og er sand hvis der skal insættes en ekstra dag i Februar.

Opret i følgende kode celle et modul der hedder Calendar.

I dette modul laver du:

  • en funktion der hedder generate_month_lengths som tager et år som parameter, og på baggrund af dette år producere et map for dette år.
  • en funktion der hedder generate som tager to parametre: min_year og max_year. Den gennemløber alle årene i mellem disse (inklusiv begge), og for hvert år kalder den generate_month_lengths. Resultatet af dette funktionskald bruges til at konstruere et map sådan at man først kan slå et år op og få resultatet af kaldet til generate_month_lengths. Dette map fra måned til årskalender returneres.

Anvend i den efterfølgende kode celle funktionen generate til at producere en kalender for årene 0 til 3000.

Næste trin …

Når du er færdig går du til næste øvelse her.