Uke 9 - Dict

Denne uken skal vi se på en ny datastruktur, dict, som er brukt ofte for å lagre strukturert data.

Først, les gjennom https://automatetheboringstuff.com/2e/chapter5/ frem til «Pretty Printing», og se på «Practice Questions 1-7»

Eksempler

Eksempel 1

Her er et eksempel på en dictionary i Python. I dette eksemplet sier den hvilket husdyr ulike personer har.

Last ned filen her: eksempel_1.py, og kjør koden. Er navnet ’keys’ eller ’values’ i pet? Er dyret ’keys’ eller ’values’ i pet?

Hva gjør koden pet['Alice']? Hva gjør koden pet['Xavier'] = 'Alien'? Hvorfor blir det feil i den siste linjen?

pet = {
    "Alice": "Dog",
    "Bob": "Cat",
    "Claire": "Stick insect",
    "Dan": "Crocodile",
    "Eve": "Elephant",
    "Fred": "Dolphin",
}

print("All pets", pet)

print(pet["Alice"])
print(pet["Eve"])

pet["Xavier"] = "Alien"

print(pet)

print(pet["Karl"])  # KeyError

Oppgave 1

I filen uke_09_oppg_1.py, lag en handleliste og skriv ut den på skjermen. Handlelisten skal være en dict med varer som keys og antall som values. Varene er:

2 brød
3 pizza
10 poteter
1 kaffe
1 ost
14 epler

Obs

Skriv svaret i det følgende formatet:

handleliste = { ... din kode her ... }
print(handleliste)

Oppgave 2

Collatz-sekvensen er definert som følger:

  1. Start med et tall \(n\).

  2. Hvis \(n\) er jevn så er neste tall \(\frac{n}{2}\), ellers så er neste tall \(3n+1\).

  3. Repetere steg 2 med de nye tallene ende til du får tallet \(1\).

Her er en funksjon som beregner Collatz-sekvensen helt til vi får \(1\), gitt en startverdi \(n\):

def collatz_sequence(n):
    sequence = [n]
    while n > 1:
        if n % 2 == 0:
            n = n // 2
        else:
            n = 3 * n + 1
        sequence.append(n)
    return sequence

I filen uke_09_oppg_2.py, skriv en funksjon som heter first_ten_collatz() som bruker collatz_sequence() til å beregne Collatz-sekvensen til tallene 1-10 og siden returnerer sekvensene i en dictionary hvor startverdiene er nøkklene. For eksempel ser starten på dictionarien slik ut:

{1: [1], 2: [2, 1], 3: [3, 10, 5, 16, 8, 4, 2, 1], 4: [4, 2, 1], ...}

Eksempel 2

Vi kan bruke .keys(), .values() og .items() sammen med en for-løkke.

Last ned filen her: eksempel_2.py, og kjør koden.

Hva looper vi over når vi bruker en for-løkke med .keys()? Hva om vi bruker .values()? Hva om vi bruker .items()?

Hva skjer i den siste løkken?

pet = {
    "Bob": "Cat",
    "Fred": "Dolphin",
    "Dan": "Crocodile",
    "Claire": "Stick insect",
    "Alice": "Dog",
    "Eve": "Elephant",
}

print("All owners")
for name in pet.keys():
    print(name)

print("\n\n\n")

print("All pets")
for animal in pet.values():
    print(animal)

print("\n\n\n")

print("All pairs")
for p in pet.items():
    print(p)

print("\n\n\n")

print("Unpack the pair tuple")
for name, animal in pet.items():
    print(f"{name} has a pet, and it is a {animal}")

Oppgave 3

I denne oppgaven skal vi jobbe med week_precip og week_temps definert her:

week_precip = {
  "monday": 0,
  "tuesday": 0.7,
  "wednesday": 0,
  "thursday": 4.7,
  "friday": 10
}

week_temps = [7.0, 8.0, 10.0, 9.0, 10.0]

I filen uke_09_oppg_3.py, skriv ut nøklene, verdiene og nøkkel/verdi-par for week_precip, og listeverdiene og index/verdi par for week_temps.

Eksempelkjøring:

Dictionary Keys:
monday
tuesday
wednesday
thursday
friday

Dictionary Values:
0
0.7
0
4.7
10

Dictionary keys/value:
monday 0
tuesday 0.7
wednesday 0
thursday 4.7
friday 10

List values:
7.0
8.0
10.0
9.0
10.0

List indices/value:
0 7.0
1 8.0
2 10.0
3 9.0
4 10.0

Eksempel 3

Metoden .get() brukes til å finne ut verdien til en gitt nøkkel. Forskjellen mellom .get() og å bruke [] er at vi ikke får KeyError om nøkkelen ikke finnes da vi bruker .get().

Last ned filen her: eksempel_3.py, og kjør koden. Skjønner du hva som skjer?

pet = {
    "Alice": "Dog",
    "Bob": "Cat",
    "Claire": "Stick insect",
    "Dan": "Crocodile",
    "Eve": "Elephant",
    "Fred": "Dolphin",
}

print(pet.get("Dan"))
print(pet.get("Karl"))  # we don't get KeyError here
print(pet.get("Karl", "Sloth"))  # we can specify the value if the key doesn't exist

Oppgave 4

Vi ser for oss at følgende dictionary representerer varebeholdningen i en bokhandel. Nøklene er titler og verdiene er antall bøker:

in_storage = {
    "Ancillary Justice": 1_046, # vi kan bruke _ i tall, den blir ignorert
    "The Use of Weapons": 372,
    "1984": 5_332,
    "The Three-Body Problem": 523,
    "A Fisherman of the Inland Sea": 728,
}

I filen uke_09_oppg_4.py, skriv et program som ta brukerinput en boktittel (bruk input() for dette) og brukes .get() i en løkke for å finne ut hvor mange eksemplarer av boka vi har.

Programmet skriver ut stringen som sier hvor mange eksemplarer av boken vi har, f.eks: Vi har 5332 av "1984".

Hvis boka ikke er på lager skal programmet skrive ut stringen Vi har 0 av [book_tittel].

Kjør løkken frem til vi får en tom streng som tittel. Da burde programmet skriver ut stringen Ha det!.

Eksempelkjøring:

>>> Tittel: 1984
Vi har 5332 av "1984"

>>> Tittel: A Fire Upon the Deep
Vi har 0 av "A Fire Upon the Deep"

>>> Tittel: Automate the Boring Stuff with Python
Vi har 0 av "Automate the Boring Stuff with Python"

>>> Tittel:
Ha det!

Eksempel 4

Vi kan bruke alle ’immutable data types’ som keys i en dictionary. Husker du hvilke datatyper er immutable?

I eksemplet her bruker vi heltall og strenger.

Last ned filen her: eksempel_4.py. Hva gjør koden?

Kjør koden og se om det var riktig.

Hvordan ser number_name ut på slutten av kjøringen?

number_name = {
    0: "zero",
    1: "one",
    2: "two",
    3: "three",
    12: "twelve",
    10: "ten",
}


def print_name(n):
    if n in number_name:
        print(f"{n} is called {number_name[n]}")
    else:
        print(f"I don't know what {n} is called.")
        name = input("Please tell me: ")
        number_name[n] = name


print_name(3)
print_name(56)

print_name(2)
print_name(56)

Oppgave 5

I denne oppgaven skal vi bruke en dictionary med informasjon om værstasjoner:

weather_stations = {
    "Bergen" : {
        "Wind speed": 3.6,
        "Wind direction": "northeast",
        "Precipitation": 5.2,
        "Device": "WeatherMaster500"
    },
    "Trondheim" : {
        "Wind speed": 8.2,
        "Wind direction": "northwest",
        "Precipitation": 0.2,
        "Device": "ClimateDiscoverer3000"
    },
    "Svalbard" : {
        "Wind speed": 7.5,
        "Wind direction": "southwest",
        "Precipitation": 1.1,
        "Device": "WeatherFinder5.0"
    },
}

I filen uke_09_oppg_5.py, lag et program som skriver ut værrapporten fra alle værstasjonene enligt følgende eksempel:

The weather in Bergen:
The wind speed is 3.6 m/s in the northeast direction and the precipitation is 5.2 mm.
The measurement was done using the WeatherMaster500 weather station.

The weather in Trondheim:
...

Eksempel 5

Metoden .setdefault() returnerer verdien til en gitt nøkkel om den finnes i dictionarien. Om den ikke finnes så putter den inn en slik nøkkel med enten verdien None eller med en verdi vi gir, og returnerer så verdien som er blitt satt inn. Du kan lese om .setdefault() i Pythons dokumentasjon.

Last ned filen her: eksempel_5.py og kjør koden. Skjønner du hva som skjer?

pet = {
    "Alice": "Dog",
    "Bob": "Cat",
    "Claire": "Stick insect",
    "Dan": "Crocodile",
    "Eve": "Elephant",
    "Fred": "Dolphin",
}

val_1 = pet.setdefault("Claire", "Rhino")
print(f"{val_1 = }")
print(f"{pet = }")

val_2 = pet.setdefault("Gunnar", "Parrot")
print(f"{val_2 = }")
print(f"{pet = }")

val_3 = pet.setdefault("Harriet")  # the default value is None
print(f"{val_3 = }")
print(f"{pet = }")

Oppgave 6

I denne oppgaven skal vi gjøre det samme som i Oppgave 4, men litt annerledes. I filen uke_09_oppg_6.py, gjør detsamme som i Oppgave 4, men hvis brukeren spør om en bok som ikke er på lager skal vi «kjøpe inn» 10 og legge til boka i in_storage med 10 eksemplarer.

Eksempelkjøring:

>>> Tittel: 1984
Vi har 5332 av "1984"

>>> Tittel: Automate the Boring Stuff with Python
Vi har 10 av "Automate the Boring Stuff with Python"

>>> Tittel:
Ha det!

Tips: bruk setdefault()

Eksempel 6

En dictionary kan f.eks brukes for å holde rede på hvor mange vi har av forskjellige ting. Her bruker vi dictionaries til å telle hvor mange ganger vi bruker ulike bokstaver og ord i begynnelsen av ’Alice in Wonderland’.

Last ned filen her: eksempel_6a.py, og kjør koden. Skjønner du hva som skjer?

I slutten av koden finner vi de 5 vanligste ordene. Prøv å bruk ’option 2’ i stedet. Som vanlig kan du finne informasjon om hvordan sorted() fungerer i Pythons dokumentasjon.

Obs

Husk at du kan bruke søkeordargumenter med funksjonene dine og Pythons innbygde funksjoner, f.eks sorted()

Last ned filen her: eksempel_6b.py, og kjør koden. Denne eksemplen er med lister, men du kan også bruke med dictionarys.

names = ["Alice", "Bob", "Claire", "Dan", "Eve", "Fred"]

# sort names by length
sorted_names = sorted(names, key=len)

print(sorted_names)

Se også i koden på variabelen sorted_result – dette er en smart bruk av søkeordargumenter for å sortere dictionarien din.

text = """Alice was beginning to get very tired of sitting by her sister
            on the bank, and of having nothing to do: once or twice she had peeped
            into the book her sister was reading, but it had no pictures
            or conversations in it, 'and what is the use
            of a book,' thought Alice 'without pictures or conversation?'"""


letter_count = {}

for l in "abcdefghijklmnopqrstuvwxyz":
    letter_count[l] = 0

for let in text:
    let = let.lower()
    if let in letter_count:  # check if key exists in dict
        letter_count[let] += 1

for let, count in letter_count.items():
    print(f"{let} is used {count:3d} times")


print("\n\n\n")


word_count = {}

for word in text.split():
    word = word.lower()
    word_count.setdefault(word, 0)
    word_count[word] += 1

for w, count in word_count.items():
    print(f"{w:14} is used {count:3d} times")


print("\n\n\n")

# The 5 most used words
# need to sort by the value in the (key,value) tuple

# option 1
def get_second(tpl):
    """Return the second element"""
    return tpl[1]


# using the keyword arguments of sorted() to sort by the second value and in reverse
sorted_result = sorted(word_count.items(), key=get_second, reverse=True)


# option 2, python has "get_second" built-in as "itemgetter"
# from operator import itemgetter
# sorted_result = sorted(word_count.items(), key=itemgetter(1), reverse=True)

print("The 5 most common words")
for w, c in sorted_result[:5]:
    print(f"{w:14} is used {c:3d} times")

Eksempel 7 - set

En relatert datatype er set, som er en usortert samling av elementer. Vi kan konstruere dem i Python med {} (uten :) eller set(). I mengder finnes det kun én av hvert verdi og det finnes ingen orden mellom verdiene.

Vi kan gjøre samme ting med mengder i Python som vi kan i mengdelære i matematikk. For eksempel kan vi finne ut unionen (elementer som finnes i én eller begge set), snittet (elementer som er felles) og differensen (elementer som ikke finnes i det andre set) mellom mengder, eller om en mengde er en delmengde av en annen mengde.

Last ned filen her: eksempel_7.py, og kjør koden. Skjønner du hva som skjer?

animals_1 = {"dog", "cat", "stick insect", "crocodile", "elephant", "dolphin"}
animals_2 = {
    "stick insect",
    "dolphin",
    "cat",
    "sloth",
    "dolphin",
}  # duplicates are ignored

print(f"{animals_1 = }")  # order is ignored
print(f"{animals_2 = }")  # order is ignored

animals_2.add("gorilla")  # we can add an element to the set
print(f"{animals_2 = }")

animals_3 = set(["cat", "elephant"])  # we can also construct sets using set()
print(f"{animals_3 = }")
print()

################################################################################

print(f"{animals_2.union(animals_3) = }")  # the union of animals_2 and animals_3

print(
    f"{animals_1.intersection(animals_2) = }"
)  # the intersection of animals_1 and animals_2

print(f"{animals_1.difference(animals_2) = }")  # animals_1 - animals_2

print(f"{animals_2.issubset(animals_1) = }")  # is animals_2 a subset of animals_1?
print(f"{animals_3.issubset(animals_1) = }")  # is animals_3 a subset of animals_1?

################################################################################

# we can turn a list into a set and then back into a list to remove duplicates
print(list(set([1, 3, 6, 3, 4, 1, 5, 3, 3])))

Oppgave 7

I filen uke_09_oppg_7.py skriv en funksjon som heter word_comparison() og som tar to ord (strenger) som argumenter. Funksjonen skal så sammenligne bokstavene i ordene og returnere en dictionary som sier hvilke bokstaver finnes i begge ordene, hvilke bokstaver som finnes i kun det første ordet og hvilke bokstaver som finnes i kun det andre ordet (som set). For eksempel skal word_comparison("computer","science") returnere følgende dictionary (husk at orden i mengder ikke spiller noen rolle):

{
  "In common": {"e", "c"},
  "Unique to first word": {"r", "u", "t", "p", "o", "m"},
  "Unique to second word": {"s", "i", "n"},
}

Oppgaver

Oppgave 1

Du finner Oppgave 1 nedenfor Eksempel 1.

Oppgave 2

Du finner Oppgave 2 nedenfor Eksempel 1.

Oppgave 3

Du finner Oppgave 3 nedenfor Eksempel 2.

Oppgave 4

Du finner Oppgave 4 nedenfor Eksempel 3.

Oppgave 5

Du finner Oppgave 5 nedenfor Eksempel 4.

Oppgave 6

Du finner Oppgave 6 nedenfor Eksempel 5.

Oppgave 7

Du finner Oppgave 7 nedenfor Eksempel 7.

Oppgave 8

Fantasy Game Inventory fra boken (https://automatetheboringstuff.com/2e/chapter5/)

Du lager et fantasy-videospill. Datastrukturen for å modellere spillerens beholdning skal være en dictionary hvor nøklene er strings som beskriver tingene i beholdningen, og verdiene er integers som forteller hvor mange av hver ting som spilleren har. For eksempel, verdiene i dictionary {'rope': 1, 'torch': 6, 'gold coin': 42, 'dagger': 1, 'arrow': 12} betyr at spilleren har 1 rope, 6 torches, 42 gold coins, etc.

Last ned filen uke_09_oppg_8_start.py og skriv resten av funksjonen displayInventory() som tar inn en dictionary av beholdningen og returneres totalt antall i formaten i eksempelkjøringen nedenfor.

Eksempelkjøring:

>>> stuff = {"rope": 1, "torch": 6, "gold coin": 42, "dagger": 1, "arrow": 12}
>>> displayInventory(stuff)
Inventory:
1 rope
6 torch
42 gold coin
1 dagger
12 arrow
62

Funksjonen skal i dette eksemplet returnere tallet \(62\) (den siste linjen).

Oppgave 9

Dragon Hoard fra boken (https://automatetheboringstuff.com/2e/chapter5/)

Skatten til en beseiret drage er representert som en liste med strenger som dette: dragonLoot = ['gold coin', 'dagger', 'gold coin', 'gold coin', 'ruby']

Fyll inn funksjonen som heter addToInventory(inventory, addedItems), hvor inventory parameteren er en dictionarien av spillerens beholdning og addedItems parameteren er en liste som f.eks dragonLoot.

addToInventory() funksjonen skal returnere en dictionary som representerer den oppdaterte beholdningen. Merk at listen over tilleggsartikler kan inneholde flere av samme element.

Last ned filen uke_09_oppg_9_start.py og skriv resten av funksjonen som heter addToInventory().

Om du kaller displayInventory() etter å ha oppdatert beholdingen med addToInventory() funksjonen skal du få ut likt som nedenfor:

>>> inv = {"gold coin": 42, "rope": 1}
>>> dragonLoot = ["gold coin", "dagger", "gold coin", "gold coin", "ruby"]
>>> inv = addToInventory(inv, dragonLoot)
>>> displayInventory(inv)
Inventory:
45 gold coin
1 rope
1 dagger
1 ruby
48

og displayInventory() skal returnere tallet \(48\).

Oppgave 10 - Euler 17

Følgende tekst er herifra: https://projecteuler.net/problem=17

If the numbers 1 to 5 are written out in words: one, two, three, four, five, then there are 3 + 3 + 5 + 4 + 4 = 19 letters used in total.

If all the numbers from 1 to 1000 (one thousand) inclusive were written out in words, how many letters would be used?

NOTE: Do not count spaces or hyphens. For example, 342 (three hundred and forty-two) contains 23 letters and 115 (one hundred and fifteen) contains 20 letters. The use of «and» when writing out numbers is in compliance with British usage.

I filen uke_09_oppg_10.py skal du gjøre følgende:

Skriv en funksjon number_name(N) som tar inn et heltall (opp til 1000) som argument N og returnerer det engelske navnet til tallet som en streng.

Tips: begynn med dict’et som finnes i Eksempel 4, og legg inn flere tall der. Du trenger ikke å ta med alle tallene frem til 1000, siden de fleste kan settes sammen. Gjerne bruk flere funksjoner for å organisere programmet ditt.

Skriv en funksjon all_numbernames(N) som tar inn et heltall (opp til 1000) som argument N og returnerer den summerte lengden av alle navnene fra 1 til N (sjekk eksemplene fra Euler-teksten). Du kan gjenbruke number_name() her.

Skriv en funksjon solve_euler_17() som returnerer den summerte lengden av alle navnene fra 1 til 1000. Du kan gjenbruke de andre funksjonene.