729G46 Informationsteknologi och programmering¶

Tema 3, Föreläsning¶

Johan Falkenjack, johan.falkenjack@liu.se¶

Föreläsningsöversikt¶

  • Info om duggan

  • Ny klass: dict
  • Mer om dataabstraktion
  • Nästlade datastrukturer
  • Bearbetning av nästlade datastrukturer
    • Fördefinierade
    • Godtyckliga

Dugga 27/10, kl. 14.00-16.00¶

  • Anmälan via LiU-appen eller Liunet.
  • Utan anmälan, ingen dugga, utanför vår kontroll. Sista anmälningsdatum 10 dagar innan duggan.
  • Tillåtet stödmaterial:
    • Kurslitteratur (bok/utskrift) utan anteckningar
    • Pythondokumentationen som PDF och websida (se kurshemsidan)
    • 1 A4-sida egna anteckningar (enkelsidigt)
    • Skrivmaterial och kladdpapper
  • Kod/Föreläsningsbilder/övriga anteckningar ej tillåtet

Dugga 27/10, kl. 14.00-16.00¶

  • Kom i tid. Duggan börjar 14.00, prick!
  • Innan måste ni ha legitimerat er, hängt av er, loggat in, fått ev. böcker kontrollerade.

Dugga 27/10, kl. 14.00-16.00¶

  • I SU-sal, ni använder Visual Studio Code + terminal
  • Personliga inställningar och hemkatalog kommer inte vara tillgängliga
  • Se till att ni vet hur ni testkör era program (gör inte misstaget att lämna in kod ni inte testat)
  • Uppgifter liknar Pythonuppgifterna, se exempeldugga på kurshemsidan

  • Kommunikation (chat) med Johan samt inlämning sker via tenta-klient
  • Assistent för hjälp med tekniska problem kommer finnas på plats

Dugga 27/10, kl. 14.00-16.00¶

  • Live-rättning, dvs duggan rättas direkt och ni får resultatet i tentaklienten.
  • 2-30 minuters väntetid beroende på hur många som lämnar in samtidigt.
  • Möjlighet att komplettera i mån av tid.
    • Räkna inte med tid att komplettera, satsa på att göra rätt direkt.

Prova-på dugga 10/10, kl. 10.15-11.00 och 11.15-12.00¶

  • Till för att prova tentasystemet, inte för att visa representativa uppgifter.
  • En person i varje labbpar kommer kl 10, den andra kommer kl 11, kom överens sinsemellan vem som ska komma vilken tid eftersom datorplatserna bara räcker till hälften av er åt gången.

IGEN: UPPGIFTERNA PÅ PROVA-PÅ-DUGGAN KOMMER INTE VARA REPRESENTATIVA!¶

SYFTET ÄR ATT PROVA SYSTEMET!¶

Repetition: for- loop eller while-loop?¶

  • Mindre logistik om vi itererar över en sekvens med for:
In [1]:
values = list(range(100))

i = 0
while i < len(values):
    value = values[i]
    if value == 42:
        print("I found the answer!")
    i += 1

for value in values:
    if value == 42:
        print("I found the answer!")
        
I found the answer!
I found the answer!
  • Notera att dessa loopar gör exakt samma sak.
  • Jag gjorde två misstag när jag skrev while-exemplet utan att ens försöka göra fel.
  • For-loopar är mer kortfattade och enklare att läsa.

Repetition: for- loop eller while-loop?

  • Behöver inte manuell loopvariabel med for:

i = 0
for value in values:
    if value == 42:
        print("I found the answer!")
    i += 1
  • Behöver inte heller en explicit uppslagning.

Repetition: Varför finns ens while-loopen?¶

  • while-loopar är nödvändiga om man vill upprepa givet ett villkor snarare än för varje element i en viss sekvens.
In [2]:
import random

secret_word = random.choice(["apelsin", "banan", "citron"])
guess = None
tries = 0

while guess != secret_word:
    if guess != None:
        print("Fel!")
    guess = input("Gissa vilket ord jag tänker på: ")
    tries += 1
print("Rätt! Det tog " + str(tries) + " försök.")
 
Gissa vilket ord jag tänker på: apelsin
Fel!
Gissa vilket ord jag tänker på: banan
Rätt! Det tog 2 försök.
  • Tveksamt om man ens kan hitta på ett sätt att lösa detta med en for-loop.
  • Jag vill försöka, men nej.

Ny datatyp: dict¶




Nycklar associerade med värden¶

  • En viktig anledning till att vi behöver ha koll på vilka datatyper som är muterbara och inte.

Dictionary¶

No description has been provided for this image
  • Dictionary eller dict i Python.
    • Mer generellt: associationslista (eng. associative array, symbol table, map)
    • Kärt barn har många namn.
  • Nyckel-värde-par
  • Värden hämtas med hjälp av nyckel istället för index som i fallet med listor och tupler.
  • Alla datatyper som är oföränderliga (immutable) kan användas som nycklar, t.ex. flyttal, heltal, strängar, tupler
  • Alla datatyper kan vara värden
  • "A Dictionary of the English Language" (1755) Samuel Johnson

Syntax¶

In [3]:
dict_example = {"key1": "value 1", 345: "value 2", 3: 54}
print(dict_example["key1"])
value 1
In [4]:
print(dict_example["key2"])
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[4], line 1
----> 1 print(dict_example["key2"])

KeyError: 'key2'
In [5]:
print(dict_example.get("key2"))
None
In [6]:
print(dict_example.get("key2", "defaultvärde om inte nyckeln finns"))
defaultvärde om inte nyckeln finns
dict_example["key1"] = "a new value"
print(dict_example)

dict_example["the answer"] = 42
print(dict_example)

Iterera över en dict¶

  • Punktnotation för att komma åt alla
    • nycklar: dict.keys()
    • värden: dict.values()
    • nyckel-värdepar: dict.items()
    • ("dict" refererar här till ett godtyckligt värde av typen dictionary, denna notation används även i pythondokumentationen)
  • Metoderna dict.keys(), dict.values() och dict.items() returnerar olika "vyer".
    • Vyer kan liknas vid temporära listor.
    • Ordningen i vyer är inte pålitlig eftersom dict är fundamentalt oordnad.

Iterera över nycklar¶

In [7]:
dict_example = {"key1": "value 1", 345: "value 2", 3: 54 }
for key in dict_example.keys():
    print(key)
    
key1
345
3

Iterera över värden¶

In [8]:
dict_example = {"key1": "value 1", 345: "value 2", 3: 54 }
for value in dict_example.values():
    print(value)
    
value 1
value 2
54

Om vi vill ha både nycklar och värden då?¶

  • Dåligt sätt:
In [9]:
dict_example = {"key1": "value 1", 345: "value 2", 3: 54 }
for key in dict_example.keys():
    print(key, ":", dict_example[key])
    
key1 : value 1
345 : value 2
3 : 54
  • Väldigt vanligt, men inte rekommenderat.

Om vi vill ha både nycklar och värden då?¶

  • Bättre sätt:
In [10]:
dict_example = {"key1": "value 1", 345: "value 2", 3: 54 }
for key, value in dict_example.items():
    print(key, ":", value)
    
key1 : value 1
345 : value 2
3 : 54
  • Många gånger snabbare och mer "Pythonic"

Dataabstraktion¶

No description has been provided for this image
  • Piet Mondrian
  • Composition II in Red, Blue, and Yellow, 1930, Kunsthaus Zürich

Abstraktion¶

  • programabstraktion: dela upp problem i delproblem, dela upp en funktion i flera funktioner
  • dataabstraktion: koppla ihop och strukturera information

Dataabstraktion¶

In [11]:
# en variabel per värde
pokemon_name = "pidgey"
ability1 = "big-pecks"
ability2 = "tangled-feet"
ability3 = "keen-eye"
  • Hur gör vi om vi vill ha information om fler eller färre "abilities" på ett smidigt sätt?

Dataabstraktion¶

In [12]:
pokemon_name = "pidgey"
# listor kan användas för att lagra 0 eller fler värden
abilities = ["big-pecks", "tangled-feet", "keen-eye"]
  • Listor kan användas för information som kan innehålla fler än ett värde.
  • Hur gör vi om vi vill ha information om flera pokemons? Ska vi ha pokemon_name1, pokemon_name2, abilities1, abilities2?

Dataabstraktion¶

In [13]:
pokemon1 = ["pidgey", ["big-pecks", "tangled-feet", "keen-eye"]]
pokemon2 = ["ditto", ["imposter", "limber"]]
In [14]:
print(pokemon1[1][1])
tangled-feet
  • Vi kan samla ihop informationen om varje pokemon till en lista samt bestämma den ordning som informationen ska komma i.

Dataabstraktion¶

In [15]:
pokemon = [ ["pidgey", ["big-pecks", "tangled-feet", "keen-eye"]],
            ["ditto", ["imposter", "limber"]] ]
In [16]:
print(pokemon[0][1][1])
tangled-feet
  • Vi kan också samla ihop alla pokemon-listor i en lista.

Dataabstraktion¶

In [17]:
pokemon = [ { "name": "pidgey",
              "abilities": ["big-pecks", "tangled-feet", "keen-eye"] },
            { "name": "ditto",
              "abilities": ["imposter", "limber"] } ]
In [18]:
print(pokemon[0]["abilities"][1])
tangled-feet
  • Vi hade även kunnat använda ett dictionary för att representera en Pokémon.

Dataabstraktion¶

In [19]:
pokemon = {"pidgey": { "name": "pidgey",
                       "abilities": ["big-pecks", "tangled-feet", "keen-eye"] },
           "ditto": { "name": "ditto",
                      "abilities": ["imposter", "limber"] }}
In [20]:
print(pokemon["pidgey"]["abilities"][1])
tangled-feet
  • Här har vi istället för en lista använt ett dictionary som har namnen på pokemons som nyckel och dictionaryt med all pokemon-information som värde.
  • Redundant att ha namn som nyckel och sen en inre dict med samma namn som värde.
  • Kanske, men i många fall har vi ett kortnamn och ett fullständigt namn.

Dataabstraktion¶

In [21]:
pokemon = {"johsj47": { "name": "johan falkenjack",
                        "abilities": ["adhd", "autism", "computer science"] } }
  • Här har vi istället för en lista använt ett dictionary som har namnen på pokemons som nyckel och dictionaryt med all pokemon-information som värde.
  • Här har vi ett extra fiktivt exempel med ett användarnamn och ett fullständigt namn

Abstrakta datatyper (ADT)¶

  • "Programming with abstract data types" (1974). Barbara Liskov & Stephen Zilles.
  • Abstrakt datatyp
    • datastruktur baserad på någon mer primitiv datatyp, t.ex. en lista
    • funktioner som används på dessa datastrukturer
    • poängen är att abstrahera bort onödig komplexitet
  • Föregångare till en objektorienterad approach.
  • Vi har redan pratat om en ADT: Stack.

Exempel på abstrakt datatyp¶

No description has been provided for this image
  • Kö (eng. queue)
    • lista med element är kön, index 0 är nästa på tur
    • funktionen create_empty_queue() som returnerar en tom kö
    • funktionen enqueue(value, queue) som lägger till värdet value till slutet på kön queue
    • funktionen dequeue(queue) som returnerar värdet som är först i kön och plockar även bort det från kön
  • Stack är LIFO - last in, first out
  • Queue är FIFO - first in, first out

En kö i Python¶

In [22]:
def create_empty_queue():
    return ('queue', [])

def enqueue(value, queue):
    if queue[0] == 'queue':
        queue[1].append(value)
    else:
        raise TypeError("Not a queue")

def dequeue(queue):
    if queue[0] == 'queue':
        if queue[1]:
            return queue[1].pop(0)
        else:
            return None
    else:
        raise TypeError("Not a queue")
    
  • Här har vi valt att explicit säga att en kö är en 2-tupel där första elementet är en identifierare, strängen "queue".
  • Eftersom varken en tupel eller en sträng är muterbar vet vi att detta aldrig ändras.

En kö i Python¶

In [23]:
q = create_empty_queue()
print(1, q)
enqueue('a', q)
print(2, q)
enqueue('b', q)
print(3, q)
print(4, f"{dequeue(q)=}")
print(5, q)
enqueue('c', q)
print(6, q)
1 ('queue', [])
2 ('queue', ['a'])
3 ('queue', ['a', 'b'])
4 dequeue(q)='a'
5 ('queue', ['b'])
6 ('queue', ['b', 'c'])
In [24]:
print(7, f"{dequeue(q)=}")
print(8, f"{dequeue(q)=}")
print(9, f"{dequeue(q)=}") 
7 dequeue(q)='b'
8 dequeue(q)='c'
9 dequeue(q)=None
  • Här har vi valt att explicit säga att en kö är en 2-tupel där första elementet är en identifierare, strängen "queue".
  • Eftersom varken en tupel eller en sträng är muterbar vet vi att detta aldrig ändras.

Stop! Tupler var ju inte muterbara!¶

No description has been provided for this image
  • Listan på index 0 kan vi däremot ändra i. Inte byta ut (det skulle innebära att tupeln ändrades), men modifiera.

Hur ser vår datastruktur ut?¶

In [25]:
q = create_empty_queue()
print(1, q)
1 ('queue', [])

Hur ser vår datastruktur ut?¶

In [26]:
q = create_empty_queue()
print(1, q)
1 ('queue', [])

Hur ser vår datastruktur ut?¶

In [27]:
enqueue('a', q)
print(2, q)
2 ('queue', ['a'])

Hur ser vår datastruktur ut?¶

In [28]:
enqueue('b', q)
print(3, q)
3 ('queue', ['a', 'b'])

Hur ser vår datastruktur ut?¶

In [29]:
print(4, f"{dequeue(q)=}")
print(5, q)
4 dequeue(q)='a'
5 ('queue', ['b'])

Hur ser vår datastruktur ut?¶

In [30]:
enqueue('c', q)
print(6, q)
6 ('queue', ['b', 'c'])

Vi kan inte lägga till, ta bort, eller byta ut ett element i en tupel...¶




...men om ett elementet i sig är muterbart, så kan vi ändra i det.¶

Bearbetning av nästlade datastrukturer¶

Nästlade datastrukturer¶

  • $A$ är en nästlad datastruktur om
    • $A$ innehåller flera värden
    • Ett av värdena i $A$ i sin tur innehåller flera värden
In [31]:
list_of_lists = [["Ada Lovelace", 1815], ["Joan Clarke", 1917]]
list_of_dicts = [ { "name": "Ada Lovelace", "birthyear": 1815 }, { "name": "Joan Clarke", "birthyear": 1815 } ]
dict_with_some_list_value = { "name": "ditto", "abilities": ["imposter", "limber"] }

Hur kommer vi åt nästlade värden?¶

In [32]:
lista1 = [["a", "b", "c"], ["d", "e", "f"]]

# första elementet i lista1
print(f"{lista1[0]=}")

# första elementet i första elementet i lista1
print(f"{lista1[0][0]=}")

# andra elementet i första elementet i lista1
print(f"{lista1[1][1]=}")
lista1[0]=['a', 'b', 'c']
lista1[0][0]='a'
lista1[1][1]='e'

Hur kommer vi åt nästlade värden?¶

In [33]:
dict1 = { "frukter": ["a", "b", "c"], 
          "bilar": ["d", "e", "f"] }

# värdet associerat med nyckeln "frukter"
print(f"{dict1['frukter']=}")

# första elementet i listan associerad med nyckeln "frukter"
print(f"{dict1['frukter'][0]=}")

# andra elementet i listan associerad med nyckeln "frukter"
print(f"{dict1['frukter'][1]=}")
dict1['frukter']=['a', 'b', 'c']
dict1['frukter'][0]='a'
dict1['frukter'][1]='b'

En nästlad loop för att bearbeta en nästlad datastruktur¶

In [34]:
outer_list = [ ["a", "b", "c"], ["d", "e", "f", "g"] ]

# om vi för varje inre lista i outer_list vill skriva ut den inre listans element?
outer_index = 0
while outer_index < len(outer_list):
    inner_list = outer_list[outer_index]
    # kod som skriver ut varje element i inner_list
    inner_index = 0
    while inner_index < len(inner_list):
        print(inner_list[inner_index])
        inner_index += 1
    outer_index += 1
a
b
c
d
e
f
g

Gaaaaah... för många index.¶


No description has been provided for this image

Vi har faktiskt någon typ av sekvenser... for!¶

In [35]:
outer_list = [ ["a", "b", "c"], ["d", "e", "f", "g"] ]

# om vi för varje inre lista i outer_list vill skriva ut den inre listans element?

for inner_list in outer_list:
    for inner_element in inner_list:
        print(inner_element)
        
a
b
c
d
e
f
g