Föreläsningsöversikt¶
Termövning 25/2Laboration 3: En egen pokedex som hämtar data från webbenOföränderliga och föränderliga värdenVariabler som referenserAssociationstabeller:dictMer om dataabstraktionNästlade datastrukturer- Bearbetning av nästlade datastrukturer
- Strängformatering med f-strängar
JSON¶
- JSON, eller JavaScript Object Notation är ett dataformat som speglar nästan exakt nästlade
dict:ar och listor i Python. - Modulen
jsonkan läsa in en fil i JSON-format och representera den just som nästladedict:ar och listor. - Svaret vi får från ett Webb-API kommer väldigt ofta i just JSON-format.
- https://www.json.org/json-sv.html
JSON: Exempel¶
- Konfigurationsfil för Pythonuppgifterna
{
"excluded_shortcodes": [],
"excluded_misc": [],
"pytest_config": {
"VENV_PATH": "/courses/TDDE44/venv_pytest",
"HELP_URL": "https://www.ida.liu.se/~TDDE44/kurslogistik/inlamningar/"
},
"assignment_sets": {
"intro": {
"name": "Introduktion",
"shortname": "Introduktion",
"outcomes": [
"Förstå vad funktioner är och hur de används.",
"Förstå vad ett returvärde är.",
"Känna till olika datatyper så som till exempel heltal, flyttal och strängar och veta hur man konverterar mellan dem.",
"Kunna använda python som en miniräknare."
],
"pass_score": 0.7,
"distinction_score": 0.7,
"point_rounding": 5,
"assignments": [
{
"type": "single",
"id": "basic/return_five",
"points": 5
},
{
"type": "single",
"id": "basic/five_twice",
"points": 5
},
...
JSON: Exempel¶
- Tentakonfiguration
{
"id": "TDDE44-DAT1-20260107",
"version": "1.3",
"config-type": "single",
"instructions": "TDDE44_v10.md",
"title": "TDDE44. DAT1. 2hp.",
"date": "2026-01-07 kl. 14.00-18.00",
"templates": {
"exam": "exam_template.md",
"part": "part_template.md",
"single_question": "single_question_template_manual_points.md",
"multi_question": "multi_question_template_manual_points.md",
"subquestion": "subquestion_template_manual_points.md",
"web_solution_exam": "hugo_web_solution_template.md",
"web_solution_part": "part_template.md",
"web_solution_single_question": "single_question_template_web_with_solution.md",
"web_solution_multi_question": "multi_question_template_web_with_solution.md",
"web_solution_subquestion": "subquestion_template_web_with_solution.md"
},
"pagebreak_after_question": true,
"unittests": "question",
"parts": [
{
"name": "Del 1",
"id": "del1",
"info": "För att bli godkänd på Del 1 måste alla uppgifter vara godkända.",
"questions": [
{
"name": "Uppgift 1 - Loopar",
"id": "uppg1",
"type": "multi",
"points": "",
"info": "- För att få godkänt på denna uppgift skall __båda__ deluppgifterna lösas.\n- En av uppgifterna skall lösas med en `while`-loop och en skall lösas med `for`-loop.\n- Comprehensions, generatorer eller rekursion får *inte* användas för att lösa uppgifterna.\n- Spara lösningarna till båda deluppgifterna i Uppgift 1 i en fil med namnet `uppg1.py`.\n- Filen skickas in via Studentklienten som '*Assignment #1*'.",
"required_score": 4,
"global_tests": [
{
"name": "Uppgift 1 - en med for, en med while",
"id": "test1",
"points": "2",
"file": "for_while"
}
],
"subquestions": [
{
"name": "Uppgift 1.1 - lös med lämplig loop",
"id": "uppg11",
"type": "single",
"points": "",
"folder": "listor",
"file": "get_repeated"
},
{
"type": "pagebreak"
},
{
"name": "Uppgift 1.2 - lös med lämplig loop",
"id": "uppg12",
"type": "single",
"points": "",
"folder": "loopar",
"file": "multiply_until_inc"
}
]
},
...
Bearbetning av nästlade datastrukturer¶
Nästlade datastrukturer¶
- Beroende på kontext kan vi vara olika precisa när vi talar om nästling.
- Mer strikt:
- $A$ är en nästlad datastruktur om $A$ är en samling och innehåller något element av samma typ som $A$.
- T.ex. listor i listor, tupler i tupler, etc.
- Mer generellt:
- $A$ är en nästlad datastruktur om $A$ är en samling och något av värdena i $A$ också är en samling.
- T.ex. listor i
dict:ar, tupler i listor, etc.
list_of_lists = [["Ada Lovelace", 1815], ["Joan Clarke", 1917]]
list_of_dicts = [ { "name": "Ada Lovelace", "birthyear": 1815 }, { "name": "Joan Clarke", "birthyear": 1917 } ]
dict_with_some_list_value = { "name": "ditto", "abilities": ["imposter", "limber"] }
Hur kommer vi åt nästlade värden?¶
- Vi kan kombinera flera uppslagningar med subskript, "utifrån och in".
lista1 = [(1, 2, 3), ('a', 'b', 'c')]
lista1[1][1]
'b'
Samma princip med dict¶
teachers = {
"chasi94": { "name": "Charlie", "courses": ["729G46", "729G87", "TDDE44", "769A34", "729G40"] },
"johsj47": { "name": "Johan", "courses": ["729G46", "729G87", "TDDE44", "TDDI18", "732G16"] }
}
def get_courses_for(liuid, staff):
return staff[liuid]['courses']
teachers['johsj47']['courses'][2]
get_courses_for('chasi94', teachers)
['729G46', '729G87', 'TDDE44', '769A34', '729G40']
Skriv ut listor av listor av strängar¶
- Skriv en funktion
print_nested_listsom kan ta en lista av listor av strängar och skriva ut strängarna. - Var lat: Använd
for
groceries = [ ["apple", "banana", "orange"], ["carrot", "onion", "potato", "garlic"] ]
def print_nested_list(outer_list):
for inner_list in outer_list:
for element in inner_list:
print(element)
print_nested_list(groceries)
apple banana orange carrot onion potato garlic
Nästlade strukturer med dict¶
Bearbeta lista av mappningar¶
teachers = {
"chasi94": { "name": "Charlie", "courses": ["729G46", "729G87", "TDDE44", "769A34", "729G40"] },
"johsj47": { "name": "Johan", "courses": ["729G46", "729G87", "TDDE44", "TDDI18", "732G16"] }
}
def print_dictionaries(dict_of_dictionaries, separator):
for dictionary in dict_of_dictionaries.values():
print(separator)
for key, value in dictionary.items():
print(f"The key '{key}' has the value: {value}")
print_dictionaries(teachers, "teacher:")
teacher: The key 'name' has the value: Charlie The key 'courses' has the value: ['729G46', '729G87', 'TDDE44', '769A34', '729G40'] teacher: The key 'name' has the value: Johan The key 'courses' has the value: ['729G46', '729G87', 'TDDE44', 'TDDI18', '732G16']
Bearbeta lista av mappningar¶
books = {
"skansholm24": {
"title": "Python från början, 2a uppl.",
"pages": 310,
"author": "Jan Skansholm"
},
"lutz25": {
"title": "Learning Python, 6th Ed.",
"author": "Mark Lutz",
"pages": 1172 } }
print_dictionaries(books, "book:")
book: The key 'title' has the value: 'Python från början, 2a uppl.' The key 'pages' has the value: '310' The key 'author' has the value: 'Jan Skansholm' book: The key 'title' has the value: 'Learning Python, 6th Ed.' The key 'author' has the value: 'Mark Lutz' The key 'pages' has the value: '1172'
Bearbeta lista av mappningar med möjliga listvärden¶
- Kan vi snygga till utskriften för när ett värde i den inre mappningen är en lista?
def print_dictionaries2(dict_of_dictionaries, separator):
for dictionary in dict_of_dictionaries.values():
print(separator)
for key, value in dictionary.items():
if type(value) == str:
print(f"The key '{key}' has the value: {value}")
elif type(value) == list:
print(f"| The key '{key}' has a list value with the following elements:")
for element in value:
print("|-", element)
print_dictionaries2(teachers, 'teacher:')
print_dictionaries2(books, 'book:')
teacher: The key 'name' has the value: Charlie | The key 'courses' has a list value with the following elements: |- 729G46 |- 729G87 |- TDDE44 |- 769A34 |- 729G40 teacher: The key 'name' has the value: Johan | The key 'courses' has a list value with the following elements: |- 729G46 |- 729G87 |- TDDE44 |- TDDI18 |- 732G16 book: The key 'title' has the value: Python från början, 2a uppl. The key 'author' has the value: Jan Skansholm book: The key 'title' has the value: Learning Python, 6th Ed. The key 'author' has the value: Mark Lutz
Leta efter värde i nästlad lista¶
- Skriv en funktion
has_valuesom kan svara på om en viss sträng förekommer i en lista eller i någon nästlad lista.
def has_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
nested_list = [ "a", ["b", ["c"]], "d", "e", ["f", "g"] ]
has_value("c", nested_list)
False
Rekursion (igen)¶
Repetition: Enkelrekursion¶
- Vi vill summera listan
[1, 2, 75, 6, 7]
def sum_list_rec(values):
# basfall
if values == []:
return 0
# förenkla och returnera
return values[0] + sum_list_rec(values[1:])
print("sum_list_rec([1, 2, 75, 6, 7]): ", sum_list_rec([1, 2, 75, 6, 7]))
sum_list_rec([1, 2, 75, 6, 7]): 91
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 returnerar värde av samma datatyp.
Rekursion för nästlade datastrukturer¶
Summera en nästlad lista av heltal¶
- Vi vill summera listan
[1, [2, 3], 4] - Vi provar att använda funktionen vi redan har:
def sum_list_rec(values):
if values == []:
return 0
else:
return values[0] + sum_list_rec(values[1:])
print("sum_list_rec([1, [2, 3], 4]): ", sum_list_rec([1, [2, 3], 4]))
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[33], line 7 4 else: 5 return values[0] + sum_list_rec(values[1:]) ----> 7 print("sum_list_rec([1, [2, 3], 4]): ", sum_list_rec([1, [2, 3], 4])) Cell In[33], line 5, in sum_list_rec(values) 3 return 0 4 else: ----> 5 return values[0] + sum_list_rec(values[1:]) Cell In[33], line 5, in sum_list_rec(values) 3 return 0 4 else: ----> 5 return values[0] + sum_list_rec(values[1:]) TypeError: can only concatenate list (not "int") to list
Fallanalys¶
- Vilka typiska fall förekommer när funktionen anropas?
- Vi antar att alla element är antingen listor eller heltal.
sum_nest([])
sum_nest([heltal, ...])
sum_nest([[...], ...])
def sum_nest(values):
if values == []:
...
if type(values[0]) == int:
...
elif type(values[0]) == list:
...
Basfallet¶
sum_nest([])- Summan av tomma listan är trivialt lika med 0.
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
...
elif type(values[0]) == list:
...
Om första elementet är ett heltal¶
sum_nest([heltal, ...])- Samma princip som tidigare: Första elementets värde, plus summan av resten av elementen.
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
...
Om första elementet är en lista, då?¶
sum_nest([[...], ...])- Vi behöver beräkna summan av alla element i första elementet, plus summan av resten av elementen. Dubbelrekursion!
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
print("sum_nest([1, [2, [3, [4]]]]): ", sum_nest([1, [2, [3, [4]]]]))
sum_nest([1, [2, [3, [4]]]]): 10
Dubbelrekursion¶
- När vi i vissa fall måste göra två rekursiva anrop när funktionen exekveras.
- All rekursion som inte är enkelrekursion kan reduceras till dubbelrekursion, om än inte alltid så enkelt.
- (Bevis lämnas som högst frivillig övning, det är okej att ta mig på orden.)
- Av det skälet kallar vi ofta all multipelrekursion för dubbelrekursion.
Nästlade strukturer kan tolkas som träd
- Träd är en fundamental relationsstruktur som dyker upp i många sammanhang:
- Avdelningar i organisationer.
- Katalogstrukturer i filsystem.
- Taxonomier
- "Parseträd" för naturliga och formella språk.
- Informationsspridning i nätverk.
- Etc.
- T.ex. för liststrukturen:
[1,[2,3],[[4,5],6]]
- Bearbetning av trädstrukturer med dubbelrekursion kallas ofta trädrekursion.
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
sum_nest([1, [2, 3], [[4, 5], 6]])
Beräknar:
sum_nest([1, [2, 3], [[4, 5], 6]])
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
sum_nest([1, [2, 3], [[4, 5], 6]])
Beräknar:
sum_nest([1, [2, 3], [[4, 5], 6]])
values[0] är lövet 1, returnera:
1 + sum_nest([[2, 3], [[4, 5], 6]])

Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + sum_nest([[2, 3], [[4, 5], 6]]
Beräknar:
sum_nest([[2, 3], [[4, 5], 6]])
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + sum_nest([[2, 3], [[4, 5], 6]]
Beräknar:
sum_nest([[2, 3], [[4, 5], 6]])
values[0] är delträdet [2, 3], returnera:
sum_nest([2, 3]) + sum_nest([[[4, 5], 6]])
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + (sum_nest([2, 3])) + sum_nest([[[4, 5], 6]])
Beräknar:
sum_nest([2, 3])
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + (sum_nest([2, 3])) + sum_nest([[[4, 5], 6]])
Beräknar:
sum_nest([2, 3])
values[0] är lövet 2, returnera:
2 + sum_nest([3])
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + (2 + sum_nest([3])) + sum_nest([[[4, 5], 6]])
Beräknar:
sum_nest([3])
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + (2 + sum_nest([3])) + sum_nest([[[4, 5], 6]])
Beräknar:
sum_nest([3])
values[0] är lövet 3, returnera:
3 + sum_nest([])
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + (2 + 3 + sum_nest([])) + sum_nest([[[4, 5], 6]])
Beräknar:
sum_nest([])
values är tomma listan, returnera:
0
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + (2 + 3 + 0) + sum_nest([[[4, 5], 6]])
Beräknar:
(2 + 3 + 0)
Uttrycket är (2 + 3 + 0), returnera:
5

Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + 5 + sum_nest([[[4, 5], 6]]
Beräknar:
sum_nest([[[4, 5], 6]])
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + 5 + sum_nest([[[4, 5], 6]]
Beräknar:
sum_nest([[[4, 5], 6]])
values[0] är delträdet [[4, 5], 6], returnera:
sum_nest([[4, 5], 6]) + sum_nest([])
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + 5 + (sum_nest([[4, 5], 6]) + sum_nest([])
Beräknar:
sum_nest([[4, 5], 6])
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + 5 + (sum_nest([[4, 5], 6]) + sum_nest([])
Beräknar:
sum_nest([[4, 5], 6])
values[0] är delträdet [4, 5], returnera:
sum_nest([4, 5]) + sum_nest([6])
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + 5 + ((sum_nest([4, 5])) + sum_nest([6])) + sum_nest([])
Beräknar:
sum_nest([4, 5])
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + 5 + ((sum_nest([4, 5])) + sum_nest([6])) + sum_nest([])
Beräknar:
sum_nest([4, 5])
values[0] är lövet 4, returnera:
4 + sum_nest([5])
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + 5 + ((4 + sum_nest([5])) + sum_nest([6])) + sum_nest([])
Beräknar:
sum_nest([5])
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + 5 + ((4 + sum_nest([5])) + sum_nest([6])) + sum_nest([])
Beräknar:
sum_nest([5])
values[0] är lövet 5, returnera:
5 + sum_nest([])
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + 5 + ((4 + 5 + sum_nest([])) + sum_nest([6])) + sum_nest([])
Beräknar:
sum_nest([])
values är tomma listan, returnera:
0
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + 5 + ((4 + 5 + 0) + sum_nest([6])) + sum_nest([])
Beräknar:
(4 + 5 + 0)
Uttrycket är (4 + 5 + 0), returnera:
9

Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + 5 + (9 + sum_nest([6])) + sum_nest([])
Beräknar:
sum_nest([6])
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + 5 + (9 + sum_nest([6])) + sum_nest([])
Beräknar:
sum_nest([6])
values[0] är lövet 6, returnera:
6 + sum_nest([])
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + 5 + (9 + 6 + sum_nest([])) + sum_nest([])
Beräknar:
sum_nest([])
values är tomma listan, returnera:
0
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + 5 + (9 + 6 + 0) + sum_nest([])
Beräknar:
(9 + 6 + 0)
Uttrycket är (9 + 6 + 0), returnera:
15

Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + 5 + 15 + sum_nest([])
Beräknar:
sum_nest([])
values är tomma listan, returnera:
0
Trädrekursion
def sum_nest(values):
if values == []:
return 0
if type(values[0]) == int:
return values[0] + sum_nest(values[1:])
elif type(values[0]) == list:
return sum_nest(values[0]) + sum_nest(values[1:])
Lösning hittills:
1 + 5 + 15 + 0
Beräknar:
1 + 5 + 15 + 0
Uttrycket är 1 + 5 + 15 + 0, returnera:
21
En alternativ lösning¶
- I
sum_nestantog vi att själva argumentet,values, alltid är en lista, men vi kan strukturera vår rekursion lite annorlunda genom att släppa det antagandet. - Vi vet dock fortfarande att vi bara har med listor och heltal att göra.
def sum_nest2(list_or_int):
if type(list_or_int) == list:
if list_or_int == []:
return 0
return sum_nest2(list_or_int[0]) + sum_nest2(list_or_int[1:])
else:
return list_or_int
print("sum_nest2([1, [2, 3], 4]): ", sum_nest2([1, [2, 3], 4]))
sum_nest2([1, [2, 3], 4]): 10
Vad händer egentligen i sum_nest2?¶
def sum_nest2(list_or_int, depth=0):
print(' '*depth*2, "Evaluating sum_nest2(", list_or_int, ")")
if type(list_or_int) == list:
if list_or_int == []:
print(' '*depth*2, "Returning from current level")
return 0
print(' '*depth*2, "Recursive call for first element")
return sum_nest2(list_or_int[0], depth+1) + sum_nest2(list_or_int[1:], depth)
else:
print(' '*depth*2, "Returning from current level")
return list_or_int
print("sum_nest2([1, [2, 3], 4]): ", sum_nest2([1, [2, 3], 4]))
Evaluating sum_nest2( [1, [2, 3], 4] )
Recursive call for first element
Evaluating sum_nest2( 1 )
Returning from current level
Evaluating sum_nest2( [[2, 3], 4] )
Recursive call for first element
Evaluating sum_nest2( [2, 3] )
Recursive call for first element
Evaluating sum_nest2( 2 )
Returning from current level
Evaluating sum_nest2( [3] )
Recursive call for first element
Evaluating sum_nest2( 3 )
Returning from current level
Evaluating sum_nest2( [] )
Returning from current level
Evaluating sum_nest2( [4] )
Recursive call for first element
Evaluating sum_nest2( 4 )
Returning from current level
Evaluating sum_nest2( [] )
Returning from current level
sum_nest2([1, [2, 3], 4]): 10
Kan vi använda iteration istället?¶
- Delvis, vi kan dock bara eliminera rekursion helt med hjälp av en manuell stack (vilket är överkurs).
- Vi antar att vi kan använda en
for-loop.- Vi behöver en ackumulatorvariabel,
result, att räkna upp summan i.
- Vi behöver en ackumulatorvariabel,
def sum_nest_for(values):
result = 0
for elem in values:
...
return result
# Fungerar för tomma listan
print("sum_nest_for([]): ", sum_nest_for([]))
sum_nest_for([]): 0
Basfall?¶
- Enklaste triviala fallet är när ingen nästlad lista existerar i
values.- Då ackumulerar vi summan för alla heltal i
valuessteg-för-steg iresult. - Eftersom detta sker implicit så behövs inget villkor för att identifiera hela basfallet.
- Då ackumulerar vi summan för alla heltal i
def sum_nest_for(values):
result = 0
for elem in values:
if type(elem) == int:
result = result + elem
...
return result
# Fungerar för listor av enbart heltal men inga nästlade listor
print("sum_nest_for([]): ", sum_nest_for([]))
print("sum_nest_for([1, 2, 3]): ", sum_nest_for([1, 2, 3]))
sum_nest_for([]): 0 sum_nest_for([1, 2, 3]): 6
Rekursionssteg¶
- När ett element i
valuesär en lista måste vi beräkna summan av den listan.- Resultatet av den beräkningen adderas till
resultpå samma sätt som enskilda heltal.
- Resultatet av den beräkningen adderas till
def sum_nest_for(values):
result = 0
for elem in values:
if type(elem) == int:
result = result + elem
elif type(elem) == list:
result = result + sum_nest_for(elem)
return result
# Fungerar för nästlade listor med heltal:
print("sum_nest_for([]): ", sum_nest_for([]))
print("sum_nest_for([1, 2, 3]): ", sum_nest_for([1, 2, 3]))
print("sum_nest_for([1, [2, 3], 4]): ", sum_nest_for([1, [2, 3], 4]))
print("sum_nest_for([1, [2, 3], [[4, 5], 6]]): ", sum_nest_for([1, [2, 3], [[4, 5], 6]]))
print("sum_nest_for([1, [2, 3], [[4, 5, [6, 7, [8]]], 9]]): ", sum_nest_for([1, [2, 3], [[4, 5, [6, 7, [8]]], 9]]))
sum_nest_for([]): 0 sum_nest_for([1, 2, 3]): 6 sum_nest_for([1, [2, 3], 4]): 10 sum_nest_for([1, [2, 3], [[4, 5], 6]]): 21 sum_nest_for([1, [2, 3], [[4, 5, [6, 7, [8]]], 9]]): 45
Leta efter ett värde i en nästlad tupel¶
def exists_in(value, values):
if values == tuple():
return False
if values[0] == value:
return True
elif type(values[0]) == tuple:
return exists_in(value, values[0]) or exists_in(value, values[1:])
else:
return exists_in(value, values[1:])
exists_in('h', ('b', ('a', (('d'), 'a'))))
False
Leta efter ett värde i en nästlad tupel, med for¶
def exists_in_for(value, values):
for element in values:
if element == value:
return True
elif type(element) == tuple:
if exists_in_for(value, element):
return True
return False
exists_in_for('h', ('b', ('a', (('d'), 'a'))))
False
Söka i en "katalogstruktur"¶
/home/student_johsj47- Vi kan representera en katalogstruktur, eller filträd, som en nästlad
dict.- För enkelhet antar vi att alla filer är tomma textfiler och representerar deras innehåll som en tom sträng.
filetree = {
'fil1': "",
'fil2': "",
'fil3': "",
'katalog1' : {
'fil5': "",
'katalog1_1': {
'fil4': ""
}
},
'katalog2': {
'fil6': "",
'hejsan': {
'hoppsan': {
'fallerallera': ""
}
}
},
'katalog3': {
'fil7': ""
}
}
- Skriv en funktion
exists(name, directory)som tar en sträng,name, och en katalogstruktur,directorysom argument. Funktionen ska besvara huruvida någon fil eller katalog med namnetnamefinns någonstans i katalogstrukturendirectory.
# Samma filträd som ovan men komprimerat
filetree = {'fil1': "", 'fil2': "", 'fil3': "", 'katalog1' :{'fil5': "", 'katalog1_1': {'fil4': ""}}, 'katalog2': {'fil6': "", 'hejsan': {'hoppsan': {'fallerallera': ""}}}, 'katalog3': {'fil7': ""}}
def exists(name, directory):
for file_node in directory.keys():
if file_node == name:
# the name we are looking for has been found
return True
if type(directory[file_node]) == dict:
# file is a directory, recursively check if the wanted name exists in that directory
if exists(name, directory[file_node]):
return True
# the end of directory has been reached without finding name
return False
print("exists('fil1', filetree) ->", exists('fil1', filetree))
print("exists('fil9', filetree) ->", exists('fil9', filetree))
print("exists('hoppsan', filetree) ->", exists('hoppsan', filetree))
exists('fil1', filetree) -> True
exists('fil9', filetree) -> False
exists('hoppsan', filetree) -> True
Söka i en "katalogstruktur", fortsättning¶
/home/student_johsj47- Vi kan representera en katalogstruktur, eller filträd, som en nästlad
dict.- För enkelhet antar vi att alla filer är tomma textfiler och representerar deras innehåll som en tom sträng.
filetree = {
'fil1': "",
'fil2': "",
'fil3': "",
'katalog1' : {
'fil5': "",
'katalog1_1': {
'fil4': ""
}
},
'katalog2': {
'fil6': "",
'hejsan': {
'hoppsan': {
'fallerallera': ""
}
}
},
'katalog3': {
'fil7': ""
}
}
- Skriv en funktion
find(name, directory)som tar en sträng,name, och en katalogstruktur,directorysom argument. Funktionen ska returnera den relativa sökvägen till någon fil eller katalog med namnetnamegivet att vi står idirectory. Om ingen sådan fil eller katalog finns någonstans idirectoryså ska funktionen returneraFalse.
# Samma filträd som ovan men komprimerat
filetree = {'fil1': "", 'fil2': "", 'fil3': "", 'katalog1' :{'fil5': "", 'katalog1_1': {'fil4': ""}}, 'katalog2': {'fil6': "", 'hejsan': {'hoppsan': {'fallerallera': ""}}}, 'katalog3': {'fil7': ""}}
def find(name, directory):
for file_node in directory.keys():
if file_node == name:
# the name we are searching for has been found
return name
if type(directory[file_node]) == dict:
# file is a directory, recursively check if the
# name searched for exists in that directory
relative_from_here = find(name, directory[file_node])
if relative_from_here:
# file was found in subdirectory, return current
# subdirectory plus the relative path from here
return file_node + "/" + relative_from_here
# the end of directory has been reached without finding name
return False
print("find('fil1', filetree)->", find('fil1', filetree))
print("find('hejsan', filetree)->", find('hejsan', filetree))
print("find('fallerallera', filetree)->", find('fallerallera', filetree))
find('fil1', filetree)-> fil1
find('hejsan', filetree)-> katalog2/hejsan
find('fallerallera', filetree)-> katalog2/hejsan/hoppsan/fallerallera
Söka i en "katalogstruktur", ännu mer fortsättning¶
/home/student_johsj47- Vi kan representera en katalogstruktur, eller filträd, som en nästlad
dict.- För enkelhet antar vi att alla filer är textfiler och representerar deras innehåll som en sträng.
filetree = {
'fil1': "Berndt Frida Henning Mio Sven Jennie Hildegard Caroline",
'fil2': "Erland Kristine Catrine Agnes Oskar Vivian Tanja Mårten",
'fil3': "Aron Edit Urban Christian Uno Gösta Hillevi Hasse",
'katalog1' : {
'fil5': "Lelle Richard Rikard Hugo Siri Torsten Solvig",
'katalog1_1': {
'fil4': "Inger Ella Tindra Laura Gunborg Ulla Hjalmar Jörgen"
}
},
'katalog2': {
'fil6': "Malin Kim Teresa Halsten Vilma Didrik Robert Alex",
'hejsan': {
'hoppsan': {
'fallerallera': "My Ylva Thea Marie Martin Linus Göta Moa"
}
}
},
'katalog3': {
'fil7': "Emmy Vilde Catharina Halsten Josefin Gunnar Beata Iris"
}
}
- Skriv en funktion
find_files_containing(search_str, directory)som tar en sträng,search_str, och en katalogstruktur,directorysom argument. Funktionen ska returnera sökvägarna till alla filer någonstans idirectorysom innehållersearch_str.
# Samma filträd som ovan men komprimerat
filetree = {'fil1': "Berndt Frida Henning Mio Sven Jennie Hildegard Caroline", 'fil2': "Erland Kristine Catrine Agnes Oskar Vivian Tanja Mårten", 'fil3': "Aron Edit Urban Christian Uno Gösta Hjalmar Hasse", 'katalog1' : {'fil5': "Lelle Richard Rikard Hugo Siri Torsten Solvig", 'katalog1_1': {'fil4': "Inger Ella Tindra Laura Gunborg Ulla Hillevi Jörgen"}}, 'katalog2': {'fil6': "Malin Kim Teresa Halsten Vilma Didrik Robert Alex", 'hejsan': {'hoppsan': {'fallerallera': "My Ylva Thea Marie Martin Linus Göta Moa"}}}, 'katalog3': {'fil7': "Emmy Vilde Catharina Halsten Josefin Gunnar Beata Iris"}}
def find_files_containing(search_str, directory):
found_files = []
for file_node, contents in directory.items():
if type(contents) == str:
# file_node är en fil så vi undersöker
# om search_str finns i den filen
if search_str in contents:
found_files.append(file_node)
if type(contents) == dict:
# file_node is a directory, recursively investigate
# the contents of that directory
found_in_subdir = find_files_containing(search_str, contents)
if found_in_subdir:
# some matching files were found in subdirectory, prepend
# current subdirectory to the relative paths from here
for relative_from_here in found_in_subdir:
found_files.append(file_node + "/" + relative_from_here)
# the end of directory has been reached without finding name
return found_files
print("find_files_containing('Halsten', filetree)->", find_files_containing('Halsten', filetree))
print("find_files_containing('H', filetree)->", find_files_containing('H', filetree))
print("find_files_containing('v', filetree)->", find_files_containing('v', filetree))
find_files_containing('Halsten', filetree)-> ['katalog2/fil6', 'katalog3/fil7']
find_files_containing('H', filetree)-> ['fil1', 'fil3', 'katalog1/fil5', 'katalog1/katalog1_1/fil4', 'katalog2/fil6', 'katalog3/fil7']
find_files_containing('v', filetree)-> ['fil1', 'fil2', 'katalog1/fil5', 'katalog1/katalog1_1/fil4', 'katalog2/hejsan/hoppsan/fallerallera']
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å.
map1 = {"a": ["b", "c"],
"b": [],
"c": ["d"],
"d": ["a"],
"e": ["b"]}
Hitta vägen¶
def get_path(s_node, e_node, a_map, visited):
"""Returnera en lista med vägen från s_node till e_node om sådan finns."""
# ä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):
"""Returnera den första vägen från en nod i s_nodes till e_node om sådan finns."""
# 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)
print(get_path('c', 'b', map1, []))
['c', 'd', 'a', 'b']
Hitta vägen (med for-loop)¶
Ibland är den bästa lösningen att kombinera rekursion med vanliga loop-konstruktioner.
def get_path_with_for(s_node, e_node, a_map, visited):
"""Returnera en lista med vägen från s_node till e_node om sådan finns."""
# ä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_with_for(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 []
print(get_path_with_for('c', 'b', map1, []))
['c', 'd', 'a', 'b']
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"]}
print(get_path_with_for('h', 'g', map2, []))
['h', 'e', 'd', 'g']
Pseudo-Quicksort, sortering med rekursion¶
- Den riktiga Quicksort-algoritmen fungerar på samma sätt, men in-place, dvs. det skapas inga nya listor
leftochrightutan snarare flyttas elementen runt ivaluesoch indexen för delsekvenserna motsvarandeleftochrightskickas med som argument. Denna funktion följer samma generella principer och illustrerar logiken bakom Quicksort, men är inte lika snabb.
def pseudoquicksort(values):
if len(values) < 2:
return values
pivot = values[0]
left = []
right = []
for elem in values[1:]:
if elem < pivot:
left.append(elem)
else:
right.append(elem)
return pseudoquicksort(left) + [pivot] + pseudoquicksort(right)
print("pseudoquicksort([8, 1, 2, 4, 0, 9])->", pseudoquicksort([8, 1, 2, 4, 0, 9]))
print("pseudoquicksort([3, 9, 1, 4, 6, 5, 2])->", pseudoquicksort([3, 9, 1, 4, 6, 5, 2]))
pseudoquicksort([8, 1, 2, 4, 0, 9])-> [0, 1, 2, 4, 8, 9] pseudoquicksort([3, 9, 1, 4, 6, 5, 2])-> [1, 2, 3, 4, 5, 6, 9]
Tidigare: Strängkonkatenering¶
- Vi kombinerar alla strängar vi behöver med
+:
namn = "Guido"
print("Hej " + namn + "!")
print("Jag vet att svaret är " + str(21*2) + ".")
resultat = sum([10, 41, 29, 74, 26, 20, 8])
print("resultat=" + str(resultat))
Hej Guido! Jag vet att svaret är 42. resultat=208
Enklare generering av strängar¶
- f-strängar, en sträng med f som prefix
f"Jag är en f-sträng"
- Kan innhålla uttryck innanför måsvingar,
{}, som beräknas när strängen skapas. - Exempel:
namn = "Guido"
print(f"Hej {namn}!")
print(f"Jag vet att svaret är {21*2}.")
resultat = sum([10, 41, 29, 74, 26, 20, 8])
print(f"resultat={resultat}")
Hej Guido! Jag vet att svaret är 42. resultat=208
Smidig utskrift av variabler¶
Lägg till
=efter ett uttryck inom{}i en f-sträng, så följer även själva uttrycket med när strängen skapas.Exempel:
resultat = sum([10, 41, 29, 74, 26, 20, 8])
print(f"{resultat=}")
resultat=208
print(f"{40+2=}")
40+2=42
my_value = 123
print(f"{my_value/2=}")
my_value/2=61.5
Formatering i f-strängar¶
- T.ex. när vi vill skriva ut tal med exakt två decimaler, även om det är fler eller färre, så använder vi modifieraren
:.2f.:separerar uttrycket från modifierare,.2fsäger "konvertera värdet till ett fixpunktstal med två decimaler".
apple_price = 1.2
print(f"Three apples cost ${3*apple_price:.2f}.")
Three apples cost $3.60.
- Det finns en uppsjö andra modifierare, t.ex. för att högerjustera värdet i ett fält av en given bredd:
apple_price = 1.2
sweater_price = 29.99
tv_price = 379
print(f"apple: {apple_price:>6.2f}")
print(f"sweater: {sweater_price:>6.2f}")
print(f"tv: {tv_price:>6.2f}")
apple: 1.20 sweater: 29.99 tv: 379.00