TDDE44 Programmering, grundkurs
Föreläsning 3.1-3.2
Jody Foo, jody.foo@liu.se
Föreläsning 3.1 & 3.2
⁃ Återkoppling från Laboration 2
⁃ Laboration 3: En egen pokedex data från webben
⁃ Oföränderliga och föränderliga värden
⁃ Referenser
⁃ Ny datatyp: dictionary
⁃ Mer om dataabstraktion
⁃ Nästlade datastrukturer
⁃ Bearbetning av nästlade datastrukturer
⁃ Rekursion
Återkoppling från laboration 2
⁃ När ska while- resp. for-loop användas?
⁃ Namngivning av variabler och funktioner
mönster, t.ex. for word in words undvik för generella namn
⁃ shebang-raden
⁃ Windows
py istället för python
ange encoding när textfil öppnas
for- loop eller while-loop?
# for-loopar är bra på att iterera genom sekvenser utan att vi behöver
# ta hand om en massa logistik for value in values:
if value == 42:
print("I found the answer!")
# du inte ta hand om någon logistik vid användning av for-loop i = 0
for value in values:
if value == 42:
print("I found the answer!") i += 1
Onödigt
for- loop eller while-loop?
import random
# while-loopar är bra om man vill fortsätta loopa under vissa
# förutsättningar - villkor
hemligt_ord = random.choice(["apelsin", "banan", "citron"]) gissat_ord = None
antal_försök = 0
while gissat_ord != hemligt_ord:
if gissat_ord != None:
print("Fel!")
gissat_ord = input("Gissa vilket ord jag tänker på: ") antal_försök += 1
print("Rätt! Det tog " + str(antal_försök) + " försök.")
Namngivning av funktioner och variabler
⁃ Försök att använda beskrivande funktions- och variabelnamn. Koden blir lättare att läsa.
⁃ Använd verbliknande namn för funktioner
add_numbers() , calculate_volume() , load_file() ,
find_most_common_word()
⁃ Använd substantiv till variabelnamn.
⁃ Bra att döpa listor till substantiv i plural: words , values ,
names
⁃ Mönster vid användning av for-loop:
for word in words
Python-skript och shebang- raden
⁃ För exekverbara textfiler i Linux, om första raden börjar på #!
(hash bang → shebang) kommer skalet att anta att efterföljande text på raden är sökvägen till den programtolk som ska
användas på resten av filen
⁃ Bash-skript
#!/bin/bash
⁃ Python-skript
#!/usr/bin/env python3
⁃ Perl-skript
#!/usr/bin/perl
Python och windows
⁃ Om man installerar Python i Windows installeras hjälpprogrammet py
⁃ py gör att shebang-rader också fungerar i windows
⁃ Vissa har märkt att de kan använda py för att köra python-
filer, men inte python3
Laboration 3
Del 1: Pythonuppgifter 3
Del 2: Egen Pokedex; hämta information från
webben
Kort om laboration 3, Del 2
⁃ Hämta information från webben istället för från fil
⁃ Nytt textbaserat data-format: JSON
⁃ Webb-API:er - om URL:er vore funktioner
⁃ PokeAPI - ett webb-API till en databas om Pokémon
⁃ pokedex.py - skript som skriver ut information om en pokémon.
⁃ Lektion 3: övningar inför Del 2
paketet requests för att hämta data från webben
JSON-data till dictionary
Oföränderliga och föränderliga värden
eng. immutable, mutable
Oföränderliga värden
⁃ I Python är strängar, heltal, flyttal, sanningsvärden, None och tupler oföränderliga (eng. immutable).
⁃ Detta betyder att man inte kan ändra på dessa värden.
t.ex. så kan inte ändra värdet 5 till värdet 1 , värdet 5 är alltid 5 .
Python behandlar strängar på samma sätt; i Python kan vi inte
ändra strängen "hej" till strängen "nej"
Oföränderliga värden
⁃ Datatypen tuple fungerar som en lista, förrutom att man inte kan byta dess element.
⁃ Parenteser används för att skapa en tuple .
l = [1, 2, 3] # lista l[0] = 5
l → [5, 2, 3]
t = (1, 2, 3) # tupel
t[0] = 5 → TypeError
Föränderliga värden
⁃ Av de datatyper som vi stött på så är tillhör listor och dictionaries (som vi stöter på strax) kategorin
förändringsbara värden (eng. mutable).
⁃ Detta betyder att vi kan ändra på dessa värden.
⁃ Vi kan t.ex. byta ut första elemenetet i listan [1, 2,3] till 5
l = [1, 2, 3]
l[0] = 5
l → [5, 2,3]
Referenser till värden
Variabler som referenser till värden
⁃ I de flesta fall är det bättre att se variabler som "etiketter"
som refererar till värden.
⁃ En variabeltilldelning
l1 = [1, 2, 3]
kan ses som att vi säger "låt etiketten l referera till värdet ..."
⁃ Vi kan låta olika etiketter referara till samma värde:
l2 = l1
Konsekvenser av att olika variabler kan referera till samma värde
l1 = [1, 2, 3]
# låt l2 referera till samma värde som l1 l2 = l1
# ändra första elementet i värdet som l1 refererar till l1[0] = "hoppsan"
# vad kommer vi få för utskrifter?
print(l1) print(l2)
Operatorn + på listor returnerar en ny lista, .append() ändrar på existerande lista
frukter_1 = ["apelsin", "banan", "citron"]
frukter_2 = frukter_1 frukter_3 = ["druva"]
# .append() -> lägg till existerande lista frukter_1.append("fikon")
# operatorn + skapar en ny lista från operanderna
# (om de är listor)
frukter_1 = frukter_1 + frukter_3
Föränderliga värden som skickas som argument till funktioner
def change_and_return_new_list(a_list):
a_list.append("list was changed") return [1, 2, 3]
my_list = ["a", "b", "c"]
# låt also_my_list också referera till värdet som my_list
# refererar till
also_my_list = my_list
# låt my_list referera till det som change_and_return_new_list
# returnerar
my_list = change_and_return_new_list(my_list) print(my_list)
print(also_my_list)
Ny datatyp: dictionary
nycklar associerade med värden
Dictionaries
⁃ Ett dictionary har element precis som en lista, men istället för index, kommer man åt elementen med via nycklar.
⁃ Varje nyckel är associerat med ett värde.
⁃ Alla datatyper som är oföränderliga (immutable) kan
användas som nycklar, t.ex. flyttal, heltal, strängar, tupler
⁃ Till skillnad från en lista, finns det ingen bestämd ordning på
nyckel-värde-paren i ett dictionary.
Dictionaries - en mängd nyckel- värde-par
# Ett dictionary omges av {} och består av nycklar och värden:
# { nyckel_1:värde1,
# nyckel_2:värde2,
# ... ,
# nyckel_n:värde_n }
# Exempel
dictionary1 = {"nyckel1":"värde 1", 345:"värde 2", 3:54 }
Ett dictionary används för att lagra värden med associativ åtkomst, dvs att
en nyckel kan användas för att komma åt ett värde. Som en lista, men med
nycklar istället för index. Som nyckel kan alla oföränderliga (immutable)
datatyper användas. Alla datatyper kan vara värden.
Istället för index använder vi nyckeln
In [1]: dictionary1 = {"nyckel1":"värde 1", 345:"värde 2", 3:54 } In [2]: dictionary1["nyckel1"]
Out[2]: 'värde 1'
In [3]: dictionary1[345]
Out[3]: 'värde 2'
In [4]: dictionary1[3]
Out[4]: 54 In [5]:
Jämfört med en lista så använder vi nyckeln istället för index för att
"komma åt" värdena.
Vi kan ändra värden genom att använda nyckeln
In [5]: dictionary1 = {"nyckel1":"värde 1", 345:"värde 2", (3):54 } In [6]: dictionary1["nyckel1"]
Out[6]: 'värde 1'
In [7]: dictionary1["nyckel1"] = 1024 In [8]: dictionary1["nyckel1"]
Out[8]: 1024
Precis som att man kan ange ett index vid tilldelning av ett värde på ett element i en lista, så kan man ange en nyckel vid tilldelning av ett
associerat värde i en dictionary.
Vi kan lägga till element till ett dictionary
In [9]: dictionary1 = {"nyckel1":"värde 1", 345:"värde 2", (3):54 } In [10]: dictionary1["ny nyckel"] = "nytt värde"
In [11]: dictionary1["böcker"] = ["bok 1", "bok 2", "bok 3"]
In [12]: dictionary1["ny nyckel"]
Out[12]: 'nytt värde'
In [13]: dictionary1["böcker"]
Out[13]: ['bok 1', 'bok 2', 'bok 3']
Vi kan lägga till nya värden i ett dictionary genom att använda en ny
nyckel.
Ny operator: in
⁃ Operatorn in kan användas bl.a. sekvenser (listor och strängar) och dictionaries.
⁃ Returnerar True om den hittar det sökta elementet i sekvensen/bland dictionaryts nycklar.
⁃ OBS! Inte samma som det in som används i samband med en for-loop
"a" in "Python är ett programmeringsspråk" → True 4 in [1, 2, 3, 4] → True
4 in [1, 2, 3, [4]] → False
"hej" in { "ja": "yes", "nej": "no", "hej": "hello" } → True
Loopa igenom ett dictionary
Alla nycklar/värden/nyckel- värdepar för ett dictionary
⁃ Punktnotation för att komma åt alla
nycklar: dict.keys()
värden: dict.values()
nyckel-värdepar: dict.items()
⁃ " dict " refererar till ett värde av typen dictionary, denna notation används även i pythondokumentationen
⁃ Funktioner som anropas via punktnotation kallas för metoder.
⁃ Metoderna dict.keys() , dict.values() och
dict.items() returnerar en "vy" in i dictionary-värdet.
Vi kan loopa genom nycklar i ett dictionary
dictionary1 = {"ett": "one", "två": "two", "bil": "car" }
# loopa genom nycklar, explicit (ingen _bestämd_ ordning finns) for key in dictionary1.keys():
print("Key: " + key)
print("Value: " + dictionary1[key])
# loopa genom nycklar, implicit (ingen _bestämd_ ordning finns) for key in dictionary1:
print("Key: " + key)
print("Value: " + dictionary1[key])
Vi kan komma åt alla nycklar antingen implicit eller explicit. Operatorn in kan också användas på nycklarna implicit:
if key_i_am_looking_for in dictionary1
Vi kan loopa genom värden i ett dictionary
# loopa genom värden, explicit (ingen _bestämd_ ordning finns) for value in dictionary1.values():
print("Value: " + value)
dict.values() , precis som dict.keys() ger en struktur (ett view objekt) som beter sig som en lista (men det är ingen riktig lista). Se
https://docs.python.org/release/3.3.0/library/stdtypes.html#dict-views
för mer information.
Vi kan loopa genom par av nycklar och värden
# loopa genom par av nycklar och värden explicit for key, value in dictionary1.items():
print("Key: " + key)
print("Value: " + value)
# en lista av tupler med två element per tupel kan loopas igenom på
# samma sätt
list_of_tuples = [("ett", 1), ("två", 2), ("tre", 3)]
for word, number in list_of_tuples:
print("word: " + word)
print("number" + str(number))
dict.items() returnerar en struktur som kan användas som en lista av
tupler med två element per tupel.
Dataabstraktion
Strukturera information
Abstraktion
⁃ programabstraktion: dela upp problem i delproblem, dela upp en funktion i flera funktioner
⁃ dataabstraktion: koppla ihop och strukturera information
Dataabstraktion
# 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
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 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
pokemon1 = ["pidgey", ["big-pecks", "tangled-feet", "keen-eye"]]
pokemon2 = ["ditto", ["imposter", "limber"]]
Vi kan samla ihop informationen om varje pokemon till en lista samt
bestämma den ordning som informationen ska komma i.
Dataabstraktion
pokemon = [ ["pidgey", ["big-pecks", "tangled-feet", "keen-eye"]], ["ditto", ["imposter", "limber"]] ]
Vi kan sedan samla ihop alla enskilda pokemons till ytterligare en
lista!
Dataabstraktion
pokemon = [ { "name": "pidgey",
"abilities": ["big-pecks", "tangled-feet", "keen-eye"] }, { "name": "ditto",
"abilities": ["imposter", "limber"] } ]
Vi hade även kunnat använda ett dictionary för att representera en
Pokémon.
Abstrakta datatyper (ADT)
⁃ Barbara Liskov, Stephen Zilles. 1974. "Programming with abstract data types"
⁃ Abstrakt datatyp
datastruktur
funktioner som används på dessa datastrukturer
⁃ Föregångare till en objektorienterad approach.
Exempel på abstrakt datatyp
⁃ Kö
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 nästa värde i
kön och plockar även bort det från kön
Bearbetning av nästlade
datastrukturer
Nästlade datastrukturer
⁃ Ett värde är en nästlad datastruktur om
värdet innehåller flera värden och
minst av värdena i sin tur innehåller flera värden
⁃ Exempel:
[["Ada Lovelace", 1815], ["Joan Clarke", 1917]]
[ { "name": "Ada Lovelace", "birthyear": 1815 }, { "name": "Joan Clarke", "birthyear": 1815 } ]
{ "name": "ditto", "abilities": ["imposter", "limber"] }
Hur kommer vi åt nästlade värden?
lista1 = [["a", "b", "c"], ["d", "e", "f"]]
# första elementet i lista1 lista1[0]
# första elementet i första elementet i lista1 lista1[0][0]
# andra elementet i första elementet i lista1 lista1[0][1]
Hur kommer vi åt nästlade värden?
dict1 = { "frukter": ["a", "b", "c"], "bilar": ["d", "e", "f"] }
# värdet associerat med nyckeln "frukter"
dict1["frukter"]
# första elementet i listan associerad med nyckeln "frukter"
dict1["frukter"][0]
# andra elementet i listan associerad med nyckeln "frukter"
dict1["frukter"][1]
Exempel på bearbetning av nästlad datastruktur
Listor i listor
En nästlad loop för att bearbeta en nästlad datastruktur
yttre_lista = [ ["a", "b", "c"], ["d", "e", "f", "g"] ]
# skriv ut varje element i listan yttre_lista index1 = 0
while index1 < len(yttre_lista):
print(yttre_lista[index1]) index1 += 1
# om vi för varje inre lista i yttre_lista vill skriva ut den
# inre listans element?
index1 = 0
while index1 < len(yttre_lista):
inre_lista = yttre_lista[index1]
# kod som skriver ut varje element i inre_lista
En nästlad loop för att bearbeta en nästlad datastruktur
yttre_lista = [ ["a", "b", "c"], ["d", "e", "f", "g"] ]
# skriv ut varje element i listan yttre_lista index1 = 0
while index1 < len(yttre_lista):
print(yttre_lista[index1]) index1 += 1
# om vi för varje inre lista i yttre_lista vill skriva ut den
# inre listans element?
index1 = 0
while index1 < len(yttre_lista):
inre_lista = yttre_lista[index1]
# kod som skriver ut varje element i inre_lista index2 = 0
while index2 < len(inre_lista):
print(inre_lista[index2]) index2 += 1
index1 += 1
En nästlad loop för att bearbeta en nästlad datastruktur
yttre_lista = [ ["a", "b", "c"], ["d", "e", "f", "g"] ]
# skriv ut varje element i listan yttre_lista for yttre_element in yttre_lista:
print(yttre_element)
# om vi för varje inre lista i yttre_lista vill skriva ut den
# inre listans element?
for yttre_element in yttre_lista:
for inre_element in yttre_element:
print(inre_element)
Exempel på bearbetning av nästlad datastruktur
Dictionaries i listor
Enklare formattering av strängar
⁃ Metoden sträng.format(arg1, ... argN)
⁃ Ersätter förekomster av "{}" med motsvarande argument
"Hej {} och {}!".format("Ada", "Charles") → "Hej Ada och Charles!"
⁃ Referera till specifikt argument med index: "{0}" refererar till första, "{1}" refererar till andra osv
"Hej {1} och {0}!".format("Ada", "Charles") → "Hej Charles och Ada!"
⁃ Argumenten till .format() kan även vara variabler.
Enklare formattering av strängar
⁃ Python 3.6 och uppåt har även stöd för sträng-litteraler, eller "f-strängar".
⁃ Exempel om namn1 = "Ada" och namn2 = "Charles"
f"Hej {namn1} och {namn2}" → "Hej Ada och Charles"
Bearbeta lista av dictionaries 1
pokemons = [ { "name": "bulbasaur", "abilities": ["chlorophyll", "overgrow"] }, { "name": "squirtle", "abilities": ["rain-dish", "torrent"] } ]
books = [ { "title": "Introduction to Python", "pages": 314 },
{ "title": "Another introduction to Python", "pages": 413 } ] def print_dictionaries(list_of_dictionaries):
# gå igenom listan av dictionaries
for dictionary in list_of_dictionaries:
# gå igenom alla nycklar i aktuellt dictionary for key in dictionary:
print(f"Key {key} has value: {dictionary[key]}") print("pokemons:")
print_dictionaries(pokemons) print("\nbooks:")
print_dictionaries(books)
Exempel på bearbetning av nästlad datastruktur
Lista med blandade datatyper
Göra olika saker beroende på datatyp
⁃ Använd funktionen type() för att ta reda på datatyp
type(5) → int
type("hejsan") → str
type(["hoppsan"]) → list
type({ "storlek": 5000 }) → dict
En nästlad loop för att bearbeta en lista med både värden som är listor och värden som inte är listor
blandad_lista = [ "a", ["b", "c"], "d", "e", ["f", "g"] ] for element in blandad_lista:
if type(element) == list:
for inre_element in element:
print(inre_element) else:
print(element)
Leta efter värde i nästlad lista
blandad_lista = [ "a", ["b", "c"], "d", "e", ["f", "g"] ] def look_for_value(needle, haystack):
for value in haystack:
if type(value) == list:
for inner_value in value:
if inner_value == needle:
return True elif value == needle:
return True return False
Exempel på bearbetning av nästlad datastruktur
Listor med dictionaries som i sin tur kan
innehålla listor
Bearbeta lista av dictionaries 2
pokemons = [ { "name": "bulbasaur", "abilities": ["chlorophyll", "overgrow"] }, { "name": "squirtle", "abilities": ["rain-dish", "torrent"] } ]
books = [ { "title": "Introduction to Python", "pages": 314 },
{ "title": "Another introduction to Python", "pages": 413 } ]
Bearbeta lista av dictionaries 2
pokemons = [ { "name": "bulbasaur", "abilities": ["chlorophyll", "overgrow"] }, { "name": "squirtle", "abilities": ["rain-dish", "torrent"] } ] books = [ { "title": "Introduction to Python", "pages": 314 },
{ "title": "Another introduction to Python", "pages": 413 } ] def print_dictionaries(list_of_dictionaries):
# gå igenom listan med dictionaries
for dictionary in list_of_dictionaries:
# gå igenom alla nycklar i varje dictionary for key in dictionary:
# i de fall som värdet för en nyckel är en lista if type(dictionary[key]) == list:
print("Value of key {} is a list:".format(key))
# gå igenom varje värde i listan som finns i dictionaryt for value in dictionary[key]:
print("- {}".format(value))
# när värdet i dictionaryt inte är en lista else:
print("Key {} has value: {}".format(key, dictionary[key])) print("pokemons:")
print_dictionaries(pokemons) print("\nbooks:")
print_dictionaries(books)
Problem med enbart loopar och nästlade strukturer
⁃ Vi behöver en loop per nivå.
⁃ Vad gör vi för att bearbeta en struktur med godtyckligt djup?
⁃ ... använd rekursion.
Rekursion
Rekursiva funktioner
⁃ En funktion är rekursiv om den anropar på sig själv direkt
eller indirekt.
Rekursion
⁃ När en funktion anropar sig själv
⁃ Vad tror ni händer nedan?
def skriv_ut_hej(heltal):
print(f"Hej {heltal}") skriv_ut_hej(heltal + 1) print("Klar!")
skriv_ut_hej(0)
Rekursion - villkor behövs för att får ett slut
def skriv_ut_hej(heltal):
if heltal == 10:
print("Klar!") else:
print(f"Hej {heltal}") skriv_ut_hej(heltal + 1) skriv_ut_hej(0)
Rekursiv problemlösning
⁃ För att lösa ett problem rekursivt, behöver vi formulera eller dela upp det på ett sådant sätt så att delarna är mindre varianter av det större problemet.
⁃ Om vi har problemet summera talen
1,
2,
75,
2och
7. Att uttrycka det som
1 + 2 + 75 + 2 + 7är att försöka lösa allt på en gång.
⁃ Ett alternativt sätt är uttrycka det som
1+ summan av de resterande talen;
2,
75,
2,
7⁃ Summan av de resterande talen i sin tur kan vi uttrycka på samma sätt,
2+ summan av de resterande talen;
75,
2,
7⁃ Vi har hittat ett sätt att formulera problemet så att vi kan
tillämpa samma lösning på mindre och mindre varianter av
samma problem.
Ett första försök
numbers = [1, 2, 75, 6, 7]
def sum_list_rec(values):
return values[0] + sum_list_rec(values[1:]) result = sum_list_rec(numbers)
print(result)
Ett första försök
numbers = [1, 2, 75, 6, 7]
def sum_list_rec(values):
return values[0] + sum_list_rec(values[1:]) result = sum_list_rec(numbers)
print(result)
# Vad är summan av alla tal i en tom lista?
Summera alla tal i en lista
numbers = [1, 2, 75, 6, 7]
def sum_list_rec(values):
# summan av värdena i en tom lista är 0 if not values:
return 0
# summan av värdena i en icke-tom lista är första värdet + summan av # resten av värdena i listan
return values[0] + sum_list_rec(values[1:]) result = sum_list_rec(numbers)
print(result)
Nytt problem: summan av alla tal från n till 0?
⁃ Summan av alla tal från n till 0 är n + summan av n-1 till 0.
Försök 1: Summan av alla tal från n till 0
def sum_rec(n):
return n + sum_rec(n-1)
Försök 1: Summan av alla tal från n till 0
def sum_rec(n):
return n + sum_rec(n-1)
# Vi behöver identifiera när vi ska sluta...
Summan av alla tal från n till 0
def sum_rec(n):
if n == 0:
return 0
return n + sum_rec(n-1)
Mönster för att skriva en rekursiv funktion
⁃ Vi letar efter det "enklaste" fallet av problemet som ska lösas, basfallet.
⁃ Basfallet är det problemfall som är så enkelt att svaret är givet.
⁃ Basfallet följs sedan av ett eller flera rekursiva anrop på en
"enklare" variant av ursprungsproblemet.
⁃ Se till att alla return -satser returnera värde av samma
datatyp.
Leta efter ett värde i en lista
numbers = [1, 2, 75, 6, 7, 75, 6, 7, 75, 6, 7]
def look_for_value_rec(value, values):
# Inget värde kan finnas i en tom lista if not values:
return False
# om första värdet är det värde vi letar efter kan vi sluta leta elif values[0] == value:
return True
# om inte första värdet var det vi letade efter returnera # resultatet av att leta efter värdet i resten av listan else:
return look_for_value_rec(value, values[1:])
print("Sökning efter 314: {}".format(look_for_value_rec(314, numbers))) print("Sökning efter 7: {}".format(look_for_value_rec(7, numbers)))
Spara bara strängar
mixed_list = ["ett", 2, "sjuttiofem", 6, 7, 75, 6, "sju"]
def keep_strings_rec(values):
# om listan är tom så resultatet en tom lista if not values:
return []
# om första värdet i listan är en sträng så är resultatet
# en lista med den strängen + alla strängar i resten av values elif type(values[0]) == str:
return [values[0]] + keep_strings_rec(values[1:])
# om första värdet i listan inte var en sträng så är resultatet # alla strängar i resten av listan
else:
return keep_strings_rec(values[1:]) print(keep_strings_rec(mixed_list))
Spara bara strängar
def keep_strings_rec(values):
if not values:
return []
elif type(values[0]) == str:
return [values[0]] + keep_strings_rec(values[1:]) else:
return keep_strings_rec(values[1:])
Varning för globala variabler
Den andra funktionen ger 0 poäng på tentan...
def keep_strings_good(values):
if not values:
return []
elif type(values[0]) == str:
return [values[0]] + keep_strings_good(values[1:]) else:
return keep_strings_good(values[1:])
answer = []
def keep_strings_bad(values):
if len(values) > 0:
if type(values[0]) == str:
answer.append(values[0]) keep_strings_bad(values[1:]) return answer
# men det funkar ju eller?
print(keep_strings_good(["ett", 2, "sjuttiofem", 6, 7])) print(keep_strings_bad(["ett", 2, "sjuttiofem", 6, 7]))
Den andra funktionen ger 0 poäng på tentan...
def keep_strings_good(values):
if not values:
return []
elif type(values[0]) == str:
return [values[0]] + keep_strings_good(values[1:]) else:
return keep_strings_good(values[1:])
answer = []
def keep_strings_bad(values):
if len(values) > 0:
if type(values[0]) == str:
answer.append(values[0]) keep_strings_bad(values[1:]) return answer
# kan vi använda funktionerna flera gånger?
print("Två anrop till keep_strings_good()")
print(keep_strings_good(["ett", 2, "sjuttiofem", 6, 7])) print(keep_strings_good(["ett", 2, "sjuttiofem", 6, 7]))
print("\nTvå anrop till keep_strings_bad()")
print(keep_strings_bad(["ett", 2, "sjuttiofem", 6, 7])) print(keep_strings_bad(["ett", 2, "sjuttiofem", 6, 7]))
Räcker det med att flytta in answer in i funktionen?
def keep_strings_bad(values):
answer = []
if len(values) > 0:
if type(values[0]) == str:
answer.append(values[0]) keep_strings_bad(values[1:]) return answer
print(keep_strings_bad(["ett", 2, "sjuttiofem", 6, 7]))
Trädrekursion
Platt lista som trädstruktur
⁃ [ 1, 2, 3]
⁃ Problem: Vad är summan av alla löv?
⁃ Alla noder är löv, dvs alla noder har värden.
def sum_rec(values):
if not values:
return 0 else:
return values[0] + sum_rec(values[1:)
[ ]
1 2 3
Platt lista som trädstruktur
⁃ [ 1, 2, 3]
⁃ Problem: Vad är summan av alla löv?
⁃ Alla noder är löv, dvs alla noder har värden.
def sum_rec(values):
if not values:
return 0 else:
return values[0] + sum_rec(values[1:)
[ ]
1 2 3
Platt lista som trädstruktur
⁃ [ 1, 2, 3]
⁃ Problem: Vad är summan av alla löv?
⁃ Alla noder är löv, dvs alla noder har värden.
⁃ Summan är värdet på detta löv + summan av övriga värden.
def sum_rec(values):
if not values:
return 0 else:
return values[0] + sum_rec(values[1:)
[ ]
1 2 3
Platt lista som trädstruktur
⁃ [ 1, 2, 3]
⁃ Problem: Vad är summan av alla löv?
⁃ Alla noder är löv, dvs alla noder har värden.
⁃ Summan är värdet på detta löv + summan av övriga värden.
def sum_rec(values):
if not values:
return 0 else:
return values[0] + sum_rec(values[1:)
[ ]
1 2 3
Platt lista som trädstruktur
⁃ [ 1, 2, 3]
⁃ Problem: Vad är summan av alla löv?
⁃ Alla noder är löv, dvs alla noder har värden.
⁃ Summan är värdet på detta löv + summan av övriga värden.
def sum_rec(values):
if not values:
return 0 else:
return values[0] + sum_rec(values[1:)
[ ]
1 2 3
Platt lista som trädstruktur
⁃ [ 1, 2, 3]
⁃ Problem: Vad är summan av alla löv?
⁃ Alla noder är löv, dvs alla noder har värden.
⁃ Summan av inga värden är 0 .
def sum_rec(values):
if not values:
return 0 else:
return values[0] + sum_rec(values[1:)
[ ]
1 2 3
Platt lista som trädstruktur
⁃ [ 1, 2, 3]
⁃ Problem: Vad är summan av alla löv?
⁃ Alla noder är löv, dvs alla noder har värden.
⁃ Summan är värdet på detta löv + summan av övriga värden.
def sum_rec(values):
if not values:
return 0 else:
return values[0] + sum_rec(values[1:)
[ ]
1 2 3
Platt lista som trädstruktur
⁃ [ 1, 2, 3]
⁃ Problem: Vad är summan av alla löv?
⁃ Alla noder är löv, dvs alla noder har värden.
⁃ Summan är värdet på detta löv + summan av övriga värden.
def sum_rec(values):
if not values:
return 0 else:
return values[0] + sum_rec(values[1:)
[ ]
1 2 3
Platt lista som trädstruktur
⁃ [ 1, 2, 3]
⁃ Problem: Vad är summan av alla löv?
⁃ Alla noder är löv, dvs alla noder har värden.
⁃ Summan är värdet på detta löv + summan av övriga värden.
def sum_rec(values):
if not values:
return 0 else:
return values[0] + sum_rec(values[1:)
[ ]
1 2
5
3Platt lista som trädstruktur
⁃ [ 1, 2, 3]
⁃ Problem: Vad är summan av alla löv?
⁃ Alla noder är löv, dvs alla noder har värden.
⁃ Summan är värdet på detta löv + summan av övriga värden.
def sum_rec(values):
if not values:
return 0 else:
return values[0] + sum_rec(values[1:)
[ ]
1
6
2 3Nästlade strukturer kan ses som trädstrukturer
⁃ [1, [2, 3], [ [4, 5], 6 ] ]
⁃ Problem: Vad är summan av alla löv?
⁃ Om nod inte är ett löv, räkna ut dess värde → summan av delträdet.
⁃ Summan av ett (del)träd är värdet på denna nod + summan av övriga noder.
[ ]
[ ] 6
4 5
1 [ ] [ ]
2 3
Nästlade strukturer kan ses som trädstrukturer
⁃ [1, [2, 3], [ [4, 5], 6 ] ]
⁃ Problem: Vad är summan av alla löv?
⁃ Om nod inte är ett löv, räkna ut dess värde -> summan av delträdet.
⁃ Summan är värdet på detta löv + summan av övriga värden.
[ ]
[ ] 6
4 5
1 [ ] [ ]
2 3
Nästlade strukturer kan ses som trädstrukturer
⁃ [1, [2, 3], [ [4, 5], 6 ] ]
⁃ Problem: Vad är summan av alla löv?
⁃ Om nod inte är ett löv, räkna ut dess värde, dvs, summan av delträdet.
⁃ Summan är värdet på detta löv + summan av övriga värden.
[ ]
[ ] 6
4 5
1 [ ] [ ]
2 3
Nästlade strukturer kan ses som trädstrukturer
⁃ [1, [2, 3], [ [4, 5], 6 ] ]
⁃ Problem: Vad är summan av alla löv?
⁃ Om nod inte är ett löv, räkna ut dess värde, dvs, summan av delträdet.
⁃ Summan är värdet på detta löv + summan av övriga värden.
[ ]
[ ] 6
4 5
1 [ ] [ ]
2
5
3Nästlade strukturer kan ses som trädstrukturer
⁃ [1, [2, 3], [ [4, 5], 6 ] ]
⁃ Problem: Vad är summan av alla löv?
⁃ Om nod inte är ett löv, räkna ut dess värde, dvs, summan av delträdet.
⁃ Summan är värdet på detta löv + summan av övriga värden.
[ ]
[ ] 6
4 5
1 [ ] [ ]
2
5
3Nästlade strukturer kan ses som trädstrukturer
⁃ [1, [2, 3], [ [4, 5], 6 ] ]
⁃ Problem: Vad är summan av alla löv?
⁃ Om nod inte är ett löv, räkna ut dess värde, dvs, summan av delträdet.
⁃ Summan är värdet på detta löv + summan av övriga värden.
[ ]
[ ] 6
4 5
1 [ ] [ ]
2
5
3Nästlade strukturer kan ses som trädstrukturer
⁃ [1, [2, 3], [ [4, 5], 6 ] ]
⁃ Problem: Vad är summan av alla löv?
⁃ Om nod inte är ett löv, räkna ut dess värde, dvs, summan av delträdet.
⁃ Summan är värdet på detta löv + summan av övriga värden.
[ ]
[ ] 6
4 5
1 [ ] [ ]
2
5
39
Nästlade strukturer kan ses som trädstrukturer
⁃ [1, [2, 3], [ [4, 5], 6 ] ]
⁃ Problem: Vad är summan av alla löv?
⁃ Om nod inte är ett löv, räkna ut dess värde, dvs, summan av delträdet.
⁃ Summan är värdet på detta löv + summan av övriga värden.
[ ]
[ ] 6
4 5
1 [ ] [ ]
2
5
315
Nästlade strukturer kan ses som trädstrukturer
⁃ [1, [2, 3], [ [4, 5], 6 ] ]
⁃ Problem: Vad är summan av alla löv?
⁃ Om nod inte är ett löv, räkna ut dess värde, dvs, summan av delträdet.
⁃ Summan är värdet på detta löv + summan av övriga värden.
[ ]
[ ] 6
4 5
1 [ ] [ ]
2 3
20
Nästlade strukturer kan ses som trädstrukturer
⁃ [1, [2, 3], [ [4, 5], 6 ] ]
⁃ Problem: Vad är summan av alla löv?
⁃ Om nod inte är ett löv, räkna ut dess värde, dvs, summan av delträdet.
⁃ Summan är värdet på detta löv + summan av övriga värden.
[ ]
[ ] 6
4 5
1 [ ] [ ]
2 3
21
Summera alla värden i en nästlad lista
def sum_of_all_values(values):
# om vi inte har några värden är summan 0 if not values:
return 0
# om noden inte är ett värde, räkna ut delträdets värde och addera det till # resten av värdena
elif type(values[0]) == list:
return sum_of_all_values(values[0]) + sum_of_all_values(values[1:]) # noden är ett löv, addera dess värde till resten av värdena i trädet else:
return values[0] + sum_of_all_values(values[1:])
# sum_of_all_values([1, [2, 3], [[4, 5], 6]])
# sum_of_all_values([1, [2, 3], [[4, 5, [6, 7, [8]]], 9]])
Leta efter ett värde i en nästlad lista
numbers1 = [[1, 2, 75, 6, 7], [75, 6, 7], [75, 6, 7], [1, 2, 75, 6, 7], [75, 6, 7], [73]]
numbers2 = [[1, 2], 75, 6, 7, [75, 6, 7], 75, 6, 7, [1, 2, 75, 6, 7], 75, 6, 7, 73]
numbers3 = [[1, 2], [75, [6, 7]], [75, [6, 7]], [75, [6, 7]], [1], 2, [75, [6, [7, [75], 6], 7]], 73]
def look_for_value_rec_all(value, values):
# Inget värde kan finnas i en tom lista if not values:
return False
# om första värdet är en lista, returnera resultatet av att # leta i både den listan och resten av values
elif type(values[0]) == list:
return (look_for_value_rec_all(value, values[0]) or look_for_value_rec_all(value, values[1:]))
# om första värdet inte är en lista, kolla om det är det värde # vi letar efter, returnera i så fall True
elif values[0] == value:
return True
# om inte första värdet varken var en lista eller det vi letade efter # returnera resultatet av att leta efter värdet i resten av listan else:
return look_for_value_rec_all(value, values[1:])
Spara bara strängar i en nästlad lista
mixed_list = ["ett", [2, "sjuttiofem", 6], 7, [[75], [6, ["sju"]]]]
def keep_strings_rec_all(values):
# om listan är tom så resultatet en tom lista if not values:
return []
# om första värdet är en lista är resultatet en lista med listan # utan några andra datatyper än strängar + bearbetningen av resten # av listan
elif type(values[0]) == list:
return ([keep_strings_rec_all(values[0])] + keep_strings_rec_all(values[1:]))
# om första värdet i listan är en sträng så är resultatet
# en lista med den strängen + alla strängar i resten av values elif type(values[0]) == str:
return [values[0]] + keep_strings_rec_all(values[1:])
# om första värdet i listan inte var en sträng så är resultatet # alla strängar i resten av listan
else:
return keep_strings_rec_all(values[1:]) print(keep_strings_rec_all(mixed_list))
Överkurs-exempel
Rekursiva funktioner kan "utforska"
flera olika lösningsvägar med trädrekursion
⁃ Exempel: En karta med enkelriktade vägar representeras som en graf.
⁃ Problem: Givet en startpunkt A kan vi ta oss till platsen B?
Returnera vägen.
⁃ Representation: Vi representerar grafen som ett dictionary där nycklarna är noder vars värde är en lista med de noder vi kan nå.
e a f
g
h
i
b c
d
Hitta vägen
map1 = {"a": ["b", "c"], "b": [],
"c": ["d"], "d": ["a"], "e": ["b"]}
def get_path(s_node, e_node, a_map, visited):
# är e_node direkt tillgänglig?
if e_node in a_map[s_node]:
return visited + [s_node, e_node]
# kolla om vi kan ta oss till e_node från någon av grannarna till s_node return get_path_hlp(a_map[s_node], e_node, a_map, visited + [s_node])
def get_path_hlp(s_nodes, e_node, a_map, visited):
# om s_nodes är tom så kan vi inte ta oss till e_node if not s_nodes:
return []
# om vi inte redan besökt s_nodes[0]
elif s_nodes[0] not in visited:
# kolla om vi kan ta oss till e_node därifrån eller någon av de övriga # nodern i s_nodes
path = get_path(s_nodes[0], e_node, a_map, visited) if path:
return path else:
return get_path_hlp(s_nodes[1:], e_node, a_map, visited) # om vi redan besökt s_nodes[0]
else:
# kolla om vi kan ta oss till e_node från någon av de övriga noderna # i s_nodes
return get_path_hlp(s_nodes[2:], e_node, a_map, visited)
a d
e b
c
Hitta vägen
map1 = {"a": ["b", "c"], "b": [],
"c": ["d"], "d": ["a"], "e": ["b"]}
def get_path(s_node, e_node, a_map, visited):
if e_node in a_map[s_node]:
return visited + [s_node, e_node]
return get_path_hlp(a_map[s_node], e_node, a_map, visited + [s_node])
def get_path_hlp(s_nodes, e_node, a_map, visited):
if not s_nodes:
return []
elif s_nodes[0] not in visited:
path = get_path(s_nodes[0], e_node, a_map, visited) if path:
return path else:
return get_path_hlp(s_nodes[1:], e_node, a_map, visited) else:
return get_path_hlp(s_nodes[2:], e_node, a_map, visited)
a d
e b
c
Hitta vägen (med for-loop)
map1 = {"a": ["b", "c"], "b": [],
"c": ["d"], "d": ["a"], "e": ["b"]}
def get_path(s_node, e_node, a_map, visited):
# är e_node direkt tillgänglig?
if e_node in a_map[s_node]:
return visited + [s_node, e_node]
# kolla om vi kan ta oss till e_node från någon av grannarna till s_node for next_node in a_map[s_node]:
if next_node not in visited:
path = get_path(next_node, e_node, a_map, visited + [s_node]) if path:
return path
# om vi kommer hit kunde vi inte hitta någon väg return []
a d
e b
c
Större karta
map2 = {"a": ["d"],
"b": ["e", "g"], "c": ["e", "h"], "d": ["g"],
"e": ["d"],
"f": ["g", "h"], "g": [],
"h": ["e", "i"],
"i": ["f"]} a
e f
g
h
i
b c
d