TDDE44 Programmering, grundkurs¶

Föreläsning 3.1-3.2¶

Johan Falkenjack, johan.falkenjack@liu.se¶

Föreläsningsöversikt¶

  • Termövning 25/2
  • Laboration 3: En egen pokedex som hämtar data från webben
  • Oföränderliga och föränderliga värden
  • Variabler som referenser
  • Associationstabeller: dict
  • Mer om dataabstraktion
  • Nä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 json kan läsa in en fil i JSON-format och representera den just som nästlade dict: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
                },
...
  • Säger inte nödvändigtvis så mycket om man inte vet hur systemet som genererar pythonuppgifterna fungerar i övrigt.

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"
            }
          ]
        },
...
  • Samma sak här, man behöver veta hur systemet fungerar för att förstå alla delar av själva datarepresentationen.

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.
In [ ]:
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".
print("lista1[0]:", lista1[0]) # första elementet i lista1
print("lista1[0][0]:", lista1[0][0]) # första elementet i första elementet i lista1
print("lista1[1][1]:", lista1[1][1]) # andra elementet i andra elementet i lista1
In [7]:
lista1 = [(1, 2, 3), ('a', 'b', 'c')]

lista1[1][1]
Out[7]:
'b'

Samma princip med dict¶

  • Vi kan jämföra med sökvägar i Linux.
# värdet associerat med nyckeln "johsj47"
print("teachers['johsj47']:", teachers['johsj47']) 
# värdet associerat med nyckeln "courses" i värdet associerat med nyckeln "johsj47"
print("teachers['johsj47']['courses']:", teachers['johsj47']['courses'])
# andra elementet i listan associerad med nyckeln "courses" i värdet associerat med nyckeln "johsj47"
print("teachers['johsj47']['courses'][1]:", teachers['johsj47']['courses'][1])
In [15]:
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)
Out[15]:
['729G46', '729G87', 'TDDE44', '769A34', '729G40']

Skriv ut listor av listor av strängar¶

  • Skriv en funktion print_nested_list som kan ta en lista av listor av strängar och skriva ut strängarna.
  • Var lat: Använd for
def print_nested_list(outer_list):
    for outer_element in outer_list:
        # kod som skriver ut varje element i inner_list
        for inner_element in outer_element:
            print(inner_element)
In [16]:
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¶

def print_dictionaries(dict_of_dictionaries, separator):
    # gå igenom alla värden i dict:en med dict:ar
    for dictionary in dict_of_dictionaries.values():
        print(separator)
        # gå igenom alla nyckel-värde-par i aktuellt dictionary
        for key, value in dictionary.items():
            print(f"The key '{key}' has the value: {value}")
In [19]:
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¶

def print_dictionaries(dict_of_dictionaries, separator):
    # gå igenom alla värden i dict:en med dict:ar
    for dictionary in dict_of_dictionaries.values():
        print(separator)
        # gå igenom alla nycklar i varje dictionary
        for key, value in dictionary.items():
            print(f"| The key '{key}' has the value: {value}")
In [18]:
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):
    # gå igenom alla värden i dict:en med dict:ar
    for dictionary in dict_of_dictionaries.values():
        print(separator)
        # gå igenom alla nycklar i varje dictionary
        for key, value in dictionary.items():
            # i de fall som värdet för en nyckel är en lista
            if type(value) == list:
                print(f"| The key '{key}' has a list value with the following elements:")
                # skriv ut varje värde i listan dictionary[key]
                for element in value:
                    print(f"| - {element}")
            # när värdet i dictionaryt inte är en lista
            else:
                print(f"| The key '{key}' has the value: {value}")
In [32]:
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_value som 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):
    # bearbeta yttre listan
    for value in haystack:
        # bearbetning av yttre värden som är listor
        if type(value) == list:
            for inner_value in value:
                if inner_value == needle:
                    return True
        # om yttre värde inte är en lista, kolla om det är det vi letar efter
        elif value == needle:
            return True
    # når vi slutet av funktionen utan att ha hittat värdet vi letar efter så
    return False
In [23]:
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
In [28]:
nested_list = [ "a", ["b", ["c"]], "d", "e", ["f", "g"] ]
has_value("c", nested_list)
Out[28]:
False

Okej, men om vi inte vet exakt hur strukturen kommer se ut i förväg?¶




Om vi måste kunna hantera godtyckligt många nivåer av nästling?¶

  • Jag ser några som skruvar nervöst på sig. Ni vet var vi är på väg?

Rekursion (igen)¶

No description has been provided for this image

Repetition: Enkelrekursion¶

  • Vi vill summera listan [1, 2, 75, 6, 7]
if values == []:
    return 0
else:
    return values[0] + sum_list_rec(values[1:])
  • Stoppvillkor och trivialt basfall.
  • Var är do_something?
  • Kombinationsoperation.
  • Rekursionssteg.
In [30]:
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:
In [33]:
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([[...], ...])
In [ ]:
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.
In [ ]:
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.
In [ ]:
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!
In [34]:
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:])
In [37]:
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.
No description has been provided for this image

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]])
No description has been provided for this image

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]])
No description has been provided for this image

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]])
No description has been provided for this image

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]])
No description has been provided for this image

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])
No description has been provided for this image

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])
No description has been provided for this image

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])
No description has been provided for this image

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([])
No description has been provided for this image

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
No description has been provided for this image

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
No description has been provided for this image

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]])
No description has been provided for this image

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([])
No description has been provided for this image

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])
No description has been provided for this image

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])
No description has been provided for this image

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])
No description has been provided for this image

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])
No description has been provided for this image

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])
No description has been provided for this image

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([])
No description has been provided for this image

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
No description has been provided for this image

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
No description has been provided for this image

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])
No description has been provided for this image

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([])
No description has been provided for this image

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
No description has been provided for this image

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
No description has been provided for this image

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
No description has been provided for this image

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
No description has been provided for this image

En alternativ lösning¶

  • I sum_nest antog 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.
  • Kan upplevas som lite enklare.
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
In [39]:
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):
    print(' '*depth*2, "Evaluating sum_nest2(", values, ")")
    if type(list_or_int) == list:
        if list_or_int == []:
            print(' '*depth*2, "Returning from current level")
            return 0
        print(' '*depth*2, "Stepping down into first 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
In [50]:
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.
In [40]:
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 values steg-för-steg i result.
    • Eftersom detta sker implicit så behövs inget villkor för att identifiera hela basfallet.
In [41]:
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 result på samma sätt som enskilda heltal.
In [42]:
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 not values:
        # Inget värde kan finnas i en tom tupel
        return False
    # Kolla om det första elementet är det värde
    # vi letar efter, returnera i så fall True
    elif values[0] == value:
        return True
    # Om första värdet är en tupel, returnera resultatet
    # av att leta i både den tupeln och resten av values
    elif type(values[0]) == tuple:
        return (exists_in(value, values[0]) or
                exists_in(value, values[1:]))
    # Om inte första värdet varken var en tupel eller det vi letade efter
    # returnera resultatet av att leta efter värdet i resten av tupeln
    else:
        return exists_in(value, values[1:])
In [45]:
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'))))
Out[45]:
False

Leta efter ett värde i en nästlad tupel, med for¶


In [49]:
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'))))
Out[49]:
False

Söka i en "katalogstruktur"¶

file_tree.png
Innehållet i /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, directory som argument. Funktionen ska besvara huruvida någon fil eller katalog med namnet name finns någonstans i katalogstrukturen directory.
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
In [52]:
# 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¶

file_tree.png
Innehållet i /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, directory som argument. Funktionen ska returnera den relativa sökvägen till någon fil eller katalog med namnet name givet att vi står i directory. Om ingen sådan fil eller katalog finns någonstans i directory så ska funktionen returnera False.
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
In [54]:
# 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¶

file_tree.png
Innehållet i /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, directory som argument. Funktionen ska returnera sökvägarna till alla filer någonstans i directory som innehåller search_str.
  • Principiellt fungerar det på samma sätt om filerna varit verkliga filer och vi behövt använda open för att undersöka deras innehåll.
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
In [56]:
# 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']

Inte bara för explicit nästling¶


Exempel: Grafsökning¶

Rekursiva funktioner kan "utforska" flera olika lösningsvägar med trädrekursion¶


No description has been provided for this image
  • 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)
In [66]:
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.

In [67]:
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 []
In [68]:
print(get_path_with_for('c', 'b', map1, []))
['c', 'd', 'a', 'b']
  • Notera att vi fortfarande har en rekursiv funktion, men genom att använda en loop för den "platta" delen av problemet har vi fått det bästa av båda världar.

Större karta¶


No description has been provided for this image
map2 = {"a": ["d"],
        "b": ["e", "g"],
        "c": ["e", "h"],
        "d": ["g"],
        "e": ["d"],
        "f": ["g", "h"],
        "g": [],
        "h": ["e", "i"],
        "i": ["f"]}
In [69]:
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 left och right utan snarare flyttas elementen runt i values och indexen för delsekvenserna motsvarande left och right skickas med som argument. Denna funktion följer samma generella principer och illustrerar logiken bakom Quicksort, men är inte lika snabb.
In [60]:
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]
In [61]:
def pseudoquicksort(seq):
    if len(seq) < 2:
        return seq
    else:
        pivot = seq[0]
        left = []
        right = []
        for elem in seq[1:]:
            if elem < pivot:
                left.append(elem)
            else:
                right.append(elem)
        return pseudoquicksort(left) + [pivot] + pseudoquicksort(right)
    
pseudoquicksort([3, 9, 1, 4, 6, 5, 2])
Out[61]:
[1, 2, 3, 4, 5, 6, 9]

f-strängar¶




Enklare strängformattering i Python¶

Tidigare: Strängkonkatenering¶

  • Vi kombinerar alla strängar vi behöver med +:
In [70]:
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
  • Långa uttryck med många +.
  • Varje enskild konkatenering skapar en ny sträng vilket är ineffektivt.
  • Vi måste manuellt konvertera värden som inte är strängar annars får vi TypeError.

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:
In [71]:
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:

In [72]:
resultat = sum([10, 41, 29, 74, 26, 20, 8])
print(f"{resultat=}")
resultat=208
In [73]:
print(f"{40+2=}")
40+2=42
In [74]:
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, .2f säger "konvertera värdet till ett fixpunktstal med två decimaler".
In [75]:
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:
In [76]:
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
  • Fixpunktstal är ett alternativt sätt att representera decimaltal (jmf. flyttal eller flytpunktstal), där vi fördefinierar antalet decimaler vi lagrar.