Skjermfilmer, Programmering

Tabellen under gir en oversikt over alle skjermfilmer det refereres til på nettstedet, under menyvalget Programmering.

Under arbeid

Artikkel Beskrivelse og film
 

Aritmetiske operasjoner
Feil og debugging
Feilhåndtering
Filbehandling
Forgreininger med «if»
for-løkker
Funksjoner
Lister
Lokale variabler
Metoder
Nøstede løkker

Strukturere «if»
Variabler
while-løkker

Lister og indekser

Kopiere strukturer

Dictionaries

 

Rekursjon Rekursjon
   
   

Kopiere dictionaries

I artikkelen om å kopiere lister ser vi at når vi tilordner ei liste til en annen variabel, blir det ikke utført noen kopiering av innholdet, den andre variabelen blir bare et nytt navn på den samme lista. Det samme er tilfelle med dictionaries.

For å illustrere bruker vi koden fra eksempel 1 i artikkelen om å kombinere dictionaries med dictionaries og lister:

agentbil = {
    "eier": "James Bond",
    "merke": "Aston Martin",
    "motordata": {"effekt": 125, "slagvolum": 2.1, "dreiemoment": 225},
    "dekkdata": {"bredde": 155, "profil": 70, "felg": 12}
}

Så skriver vi

nybil = agentbil

Da blir nybil en referanse til samme dictionary som agentbil, som vi kan se i Python Tutor:

Illustrasjon av flere referanser til samme dictionary

På samme måte som lister, har også dictionaries en copy()-metode, som genererer en ny dictionary. Hvis vi skriver

nybil = agentbil.copy()

blir nybil en ny dictionary, som vi kan se i Python Tutor:

Illustrasjon av grunn kopi av dictionary

Vi ser imidlertid at dictionaries med nøkler "motordata" og "dekkdata" inni agentbil ikke er blitt kopiert, det er bare gjort en grunn kopiering. Vil vi gjøre en dyp kopiering, kan vi bruke funksjonen deepcopy() fra modulen copy, på samme måte som med lister, slik det beskrives i artikkelen om grunn og dyp kopiering. Skriver vi

from copy import deepcopy
 
nybil = deepcopy(agentbil)

blir også dictionaries inni agentbil kopiert, som vi kan se i Python Tutor:

Illustrasjon av dyp kopi av dictionary

Kilder

    • Sweigart A. (2021). Beyond the basic stuff with Python. no starch press

Kombinere dictionaries med dictionaries og lister

Akkurat som vi kan ha lister inni lister, kan vi ha dictionaries inni dictionaries. Eksempel 1 har en dictionary som heter agentbil, der verdiene til nøklene "motordata" og "dekkdata" er egne dictionaries.

Eksempel 1:

agentbil = {
    "eier": "James Bond",
    "merke": "Aston Martin",
    "motordata": {"effekt": 125, "slagvolum": 2.1, "dreiemoment": 225},
    "dekkdata": {"bredde": 155, "profil": 70, "felg": 12}
}

I Python Tutor ser det slik ut:

Illustrasjon av dictionary i dictionary

For å referere til en nøkkel i en dictionary inni en dictionary må vi bruke 2 indekser, én for hver dictionary. For eksempel agentbil["dekkdata"]["bredde"] for å finne dekkbredden til agentbil i eksempel 1.

Vi kan også ha dictionaries i lister. Eksempel 2 har ei liste som inneholder dictionaries som kopler postnummer og sted.

Eksempel 2:

postinfo = [
    {"nummer": 4225, "navn": "Lyngdal"},
    {"nummer": 4586, "navn": "Korshamn"},
    {"nummer": 4590, "navn": "Snartemo"},
    {"nummer": 4596, "navn": "Eiken"}
]

I Python Tutor ser det slik ut:

Illustrasjon av dictionary i liste

For å referere til en nøkkel i en dictionary inni ei liste, må vi bruke 2 indekser, én for lista og én for dictionary. For eksempel vil postinfo[1]["navn"] i eksempel 2 være "Korshamn" fordi element nummer 1 i postinfo inneholder en dictionary med nøkkel-verdi-paret "navn": "Korshamn".

Koden under vil gjennomløpe lista i eksempel 2 og skrive ut informasjon om postnummer og navn.

for sted in postinfo:
    print(f"{sted['navn']} har postnummer {sted['nummer']}.")

Vi kan også lister i dictionaries. Eksempel 3 inneholder en dictionary med forfattere som nøkler og lister med boktitler som verdier.

Eksempel 3:

katalog = {
    "Knut Hamsun": ["Sult", "Markens grøde", "Victoria"],
    "Ernest Hemingway": ["Og solen går sin gang", "Den gamle mannen og havet"],
    "Roald Dahl": ["Charlie og sjokoladefabrikken", "Heksene", "Matilda", "SVK"]
}

I Python Tutor ser det slik ut:

Illustrasjon av lister i dictionary

For å referere til et element i ei liste inni en dictionary, må vi bruke 2 indekser, én for dictionary og én for lista. For eksempel vil katalog["Roald Dahl"][1] i eksempel 3 være "Heksene" fordi element nummer 1 i lista knyttet til nøkkel "Roald Dahl", inneholder "Heksene".

Koden under vil gjennomløpe lista i eksempel 3 og skrive ut informasjon om forfattere og bøker.

for forfatter, titler in katalog.items():
    print(f"{forfatter} har skrevet:", end = " ")
    for tittel in titler:
        print(f"'{tittel}'", end = " ")
    print()

Det er ingen teoretisk grense for hvor mange dictionaries og lister vi kan nøste inni hverandre, men i praksis blir det sjelden bruk for mer enn 2-3 nivåer.

Kilder

    • Matthes A. (2019). Python Crash Course. no starch press

Grunn og dyp kopiering

I eksempel 1 i artikkelen om lister som inneholder lister har vi ei liste som inneholder tre andre lister:

grader = [[12, 10, 14, 7, 8, 11, 10], [10, 7, 9, 6, 6, 8, 15], [10, 5, 3, 6, 4, 3, 2]]

I Python Tutor ser det slik ut:

Illustrasjon av lister i lister

Så skriver vi

grader_kopi = grader

Som vi ser i artikkelen om å kopiere lister, blir da grader_kopi bare en ny referanse til samme liste:

Illustrasjon av flere referanser til samme liste

Men som vi også ser i i artikkelen om å kopiere lister, hvis vi skriver

grader_kopi = grader[:]

eller

grader_kopi = grader.copy()

blir grader_kopi ei ny liste:

Illustrasjon av grunn kopi av liste

Imidlertid ser vi at de tre listene inni grader ikke er blitt kopiert. grader_kopi er blitt ei ny liste, men den refererer til de samme tre listene som grader. Denne måten å kopiere lister på gir nemlig bare det vi kaller grunn kopiering. Selve lista grader blir kopiert, men ikke listene den refererer til.

Det motsatte av grunn kopiering er dyp kopiering. Da kopieres også listene det refereres til, ikke bare det øverste nivået. For å utføre dyp kopiering kan vi bruke funksjonen deepcopy() fra modulen copy. Hvis vi skriver

from copy import deepcopy

grader_kopi = deepcopy(grader)

ser resultatet slik ut:

Illustrasjon av dyp kopi av liste

Nå har vi også fått kopier av de tre listene inni grader.

Kilder

    • Sweigart A. (2021). Beyond the basic stuff with Python. no starch press

Lister som inneholder lister

Ei liste kan inneholde elementer av alle datatyper. Vi har sett eksempler på lister med heltall, flyttall, strenger og boolske variabler. Men ei liste kan også inneholde andre lister.

Eksempel 1:

Vi har målt maksimumstemperaturen i en by hver dag i tre uker, og har laget følgende oversikt

  Dag 1 Dag 2 Dag 3 Dag 4 Dag 5 Dag 6 Dag 7
Uke 1 12 10 14 7 8 11 10
Uke 2 10 7 9 6 6 8 15
Uke 3 10 5 3 6 4 3 2

Her kan vi representere hver uke som ei liste med 7 elementer i Pyton:

grader_uke_1 = [12, 10, 14, 7, 8, 11, 10]
grader_uke_2 = [10, 7, 9, 6, 6, 8, 15]
grader_uke_3 = [10, 5, 3, 6, 4, 3, 2]

Disse listene kan vi så sette inn i ei ny liste:

grader = [grader_uke_1, grader_uke_2, grader_uke_3]

I Python Tutor ser det slik ut:

Illustrasjon av lister i lister

Vi ser at grader_uke_1, grader_uke_2 og grader_uke_3 refererer til hver si liste, mens grader referer til ei liste som igjen refererer til grader_uke_1, grader_uke_2 og grader_uke_3.

Vi kan for så vidt også opprette grader uten å gå veien om ukelistene:

grader = [[12, 10, 14, 7, 8, 11, 10], [10, 7, 9, 6, 6, 8, 15], [10, 5, 3, 6, 4, 3, 2]]

I eksempel 1 har vi ei liste som inneholder 3 elementer som hver er lister med 7 elementer. For å referere til et enkeltelement må vi da bruke to indekser. Én for å velge ei av listene, og én for å velge et element i denne lista igjen. Indeksene starter på 0, så hvis vi har opprettet grader slik som i eksempel 1, og skriver

print(grader[0][2])

får vi skrevet ut 14 fordi element nummer 0 i grader er lista grader_uke_1, og element nummer 2 i grader_uke_1 er 14.

Generelt vil indeksene [m][n] gi et element med indeks n i lista med indeks m.

Oppgave 1:

Basert på lista grader fra eksempel 1, avgjør hva som blir skrevet ut hvis vi kjører følgende kode:

print(grader[1][2])
print(grader[2][1])
print(grader[0])

Gjør oppgaven på papir først, og sjekk så ved å kjøre koden om du har rett.

Se løsningsforslag

Vil vi gjennomløpe alle elementene i ei liste med lister, må vi ha to nøstede løkker.

Eksempel 2:

Vi har opprettet lista grader som i eksempel 1, og skal finne den høyeste temperaturen i løpet av de tre ukene. Det kan vi gjøre ved hjelp av følgende Python-kode:

# Finner høyeste tall i liste med lister
maks_grader = grader[0][0]  # Startverdi lik dag 1, uke 1
for grader_uke in grader:  # Uke for uke
    for grader_dag in grader_uke:  # Dag for dag i uke
        if grader_dag > maks_grader:
            maks_grader = grader_dag
print(f"Høyeste temperatur var {maks_grader} grader.")

I eksempel 2 tar vi utgangspunkt i temperaturen første dag i første uke, og leter så gjennom resten av lista for å se om vi finner noe høyere. Her bruker vi altså to nøstede løkker. Koden

for grader_uke in grader:

vil tilordne liste for liste fra grader til grader_uke, ei liste for hvert gjennomløp i den ytre løkka.

I den indre løkka vil koden

for grader_dag in grader_uke:

tilordne tall for tall i lista grader_uke til grader_dag, ett tall for hvert gjennomløp i den indre løkka.

Legg merke til at grader_uke i den ytre løkka er ei liste, mens grader_dag i den indre løkka er et tall.

Vi kan ikke bruke Python-funksjonen max() til å finne den høyeste temperaturen. max(grader) vil returnere lista for uka med den høyeste summen av temperaturer i stedet for den høyeste enkelttemperaturen.

Det er ikke noen teoretisk grense for hvor mange lister vi kan ha inni hverandre, men i praksis er det sjelden behov for mer enn 2-3.

Lister som ligger inni ei annen liste, trenger ikke være like store.

Eksempel 3:

Koden under oppretter lista diverse, som inneholder 2 lister med henholdsvis 3 og 5 elementer.

diverse = [[1, 2, 3], [1, 2, 3, 4, 5]]

Kilder

    • Matthes A. (2019). Python Crash Course. no starch press

Den som er vant med å programmere i språk som C, vil kanskje lure på hvorfor vi ikke har pekere i Python. Men ser vi på bildet fra Python Tutor over, skjønner vi at vi har pekere, de er bare implisitte. I eksempel 1 er for eksempel grader en peker til ei liste som inneholder pekere til tre andre lister.

Sortere lister

I artikkelen om liste-metoder blir vi kjent med en del av metodene som er knyttet til lister.

Lister har også en metode for sortering, sort().

Eksempel 1:

Vi oppretter ei liste med tall i vilkårlig rekkefølge, skriver den ut, sorterer den og skriver den ut på nytt.

kaos = [6, -3, 0, 5, 2, 8]
print(kaos)
kaos.sort()
print(kaos)

Vi får først skrevet ut lista slik vi opprettet den, deretter sortert som [−3, 0, 2, 5, 6, 8].

Det er også mulig å få sort() til å sortere i synkende rekkefølge ved å gi inn True som et argument til parameteren reverse.

Eksempel 2:

Vi gjør det samme som i eksempel 1, men angir reverse = True.

kaos = [6, -3, 0, 5, 2, 8]
print(kaos)
kaos.sort(reverse = True)
print(kaos)

Vi får først skrevet ut lista slik vi opprettet den, deretter sortert synkende som [8, 6, 5, 2, 0, −3].

Vi ser at metoden sort() endrer på den opprinnelige lista. Ønsker vi å beholde lista uendret og i stedet få en sortert kopi, kan vi i stedet bruke Python-funksjonen sorted(). Også med sorted() kan vi sortere i synkende rekkefølge ved gi inn True som et argument til parameteren reverse.

Vi skriver følgende kode:

kaos = [6, -3, 0, 5, 2, 8]
print(kaos)
print(sorted(kaos))
print(sorted(kaos, reverse = True))
print(kaos)

Vi får da først skrevet ut den opprinnelige lista, deretter lista sortert stigende og synkende, og i siste utskrift ser vi at den opprinnelige lista er uendret.

Det finnes også en metode som snur rekkefølgen i ei liste, reverse().

Eksempel 4:

Vi skriver følgende kode:

kaos = [6, -3, 0, 5, 2, 8]
print(kaos)
kaos.reverse()
print(kaos)

Vi får først skrevet ut lista slik vi opprettet den, deretter i omvendt rekkefølge som [8, 2, 5, 0, −3, 6].

Oppgave 1:

Skriv Python-kode som oppretter lista kaos som vist i eksemplene, og deretter sorterer lista synkende uten at du angir reverse = True.

Se løsningsforslag

Kilder

    • Matthes A. (2019). Python Crash Course. no starch press

Kopiere lister

Vi vet at vi kan tilordne verdier fra én variabel til en annen.

Har vi for eksempel variabelen x, som har verdien 3, og skriver

y = x

opprettes det en ny variabel som heter y og får verdien 3.

Endrer vi senere verdien til x, påvirker ikke det verdien y har fått.

Når det gjelder lister, fungerer imidlertid tilordningsmekanismen annerledes. Skriver vi for eksempel

liste_a = [1, 2, 3, 4]
liste_b = liste_a

blir det opprettet en ny variabel som heter liste_b, men selve innholdet i liste_a blir ikke tilordnet liste_b. I stedet blir liste_b en ny variabel som refererer til det samme som liste_a.

Eksempel 1:

Vi kjører følgende Python-kode:

liste_a = [1, 2, 3, 4]
liste_b = liste_a
liste_b[1] = "hei"
print(liste_a)
print(liste_b)

I eksempel 1 oppretter vi først liste_a, så setter vi liste_b lik liste_a, deretter endrer vi et element i liste_b. Når vi så skriver ut både liste_a og liste_b, ser vi at endringen vi gjorde i liste_b også er blitt gjort i liste_a. Grunnen er at liste_a og liste_b egentlig ikke er to forskjellige lister, men bare to forskjellige navn på samme liste.

Bruker vi Python Tutor, ser vi at liste_a og liste_b refererer til samme liste:

Illustrasjon av to lister som er samme objekt

Vil vi kopiere innholdet i ei liste, kan vi enten bruke [:] som indeks for å lage ei del-liste med alle elementene, eller vi kan bruke listemetoden copy().

Eksempel 2:

Vi kjører følgende Python-kode:

liste_a = [1, 2, 3, 4]
liste_b = liste_a[:]
liste_b[1] = "hei"
print(liste_a)
print(liste_b)

og

liste_a = [1, 2, 3, 4]
liste_b = liste_a.copy()
liste_b[1] = "hei"
print(liste_a)
print(liste_b)

I begge tilfeller i eksempel 2 ser vi at endringen vi gjør i liste_b ikke påvirker liste_a. Det er fordi liste_b er blitt ei separat liste med sitt eget innhold.

Bruker vi Python Tutor, ser vi at liste_a og liste_b nå er to forskjellige lister:

Illustrasjon av to lister som er forskjellig objekt

I artikkelen om lokale og globale variabler lærer vi at variabler i funksjoner er lokale, med følgende eksempel:

Vi har en funksjon, kvadrer(), som kvadrerer verdien til parameteren x:

def kvadrer(x):
    x = x**2

I hovedprogrammet oppretter vi så en variabel, x, med verdien 4, kaller opp funksjonen med x som argument, og skriver ut verdien til x etterpå:

x = 4
kvadrer(x)
print(x)

Vi ser da at x utenfor funksjonen ikke endrer seg.

Når vi kaller opp en funksjon med ei liste som argument, er det imidlertid bare en referanse til lista som overføres til funksjonsparameteren.

Eksempel 3:

Vi har funksjonen kvadrer, som opphøyer alle elementer i ei liste i andre:

def kvadrer(liste):
    for indeks in range(len(liste)):
        liste[indeks] **= 2

I hovedprogrammet oppretter vi så lista siffer med elementene 1, 2, 3, kaller opp kvadrer() med siffer som argument, og skriver ut innholdet etterpå:

siffer = [1, 2, 3]
kvadrer(siffer)
print(siffer)

Vi ser at innholdet i siffer er blitt kvadrert, enda vi ikke har koplet siffer til noen returverdi fra funksjonen.

Bruker vi Python Tutor, ser vi at siffer i hovedprogrammet og liste i kvadrer() refererer til samme liste:

Illustrasjon av hovedprogram og funksjon som refererer til lister som er samme objekt

Oppgave 1:

I eksempel 3 kalte vi opp funksjonen kvadrer() med siffer som argument på denne måten:

kvadrer(siffer)

Vi så da et endringene som kvadrer() gjorde på sin parameter, også hadde effekt på siffer. Hvordan kan vi skrive om oppkallet av funksjonen slik at siffer ikke endres?

Se løsningsforslag

Kilder

    • Matthes A. (2019). Python Crash Course. no starch press

Deler av lister

Det er mulig til å referere til deler av ei liste ved å skrive inn to indekser med kolon mellom, der [a:b] gir ei liste som består av elementene med indeks fra og med a inntil b.

Eksempel 1:

Vi oppretter ei liste som heter primtall:

primtall = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

Skriver vi så for eksempel

print(primtall[1:4])

får vi skrevet ut ei del-liste som består av elementene med indeks 1, 2 og 3 i primtall, det vil si [3, 5, 7].

Det er viktig å merke seg at indekser på formen [a:b] gir ei ny liste, ikke enkeltelementer.

Utelater vi a, betyr det at vi skal starte på første element i lista, utelater vi b, betyr det at vi skal slutte på siste element i lista.

Eksempel 2:

Vi oppretter ei liste med 10 elementer som heter primtall:

primtall = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

Skriver vi så for eksempel

print(primtall[3:4])

får vi skrevet ut ei del-liste som består av elementet med indeks 3 i primtall, det vil si [7].

Skriver vi

print(primtall[:4])

får vi skrevet ut ei del-liste som består av elementene med indekser 0, 1, 2 og 3 i primtall, det vil si [2, 3, 5, 7].

Skriver vi

print(primtall[4:])

får vi skrevet ut ei del-liste som består av elementene med indekser 4, 5, 6, 7, 8 og 9 i primtall, det vil si [11, 13, 17, 19, 23, 29].

Skriver vi

print(primtall[:])

får vi skrevet ut ei del-liste som består av alle elementene i primtall, det vil si [2, 3, 5, 7, 11, 13, 17, 19, 23, 29].

På samme måte som i range(), kan vi angi en skrittverdi når vi genererer del-lister. [a:b:c] gir ei liste som består av elementene med indeks fra og med a inntil b i skritt på c.

Eksempel 3:

Vi oppretter ei liste som heter primtall, som i eksempel 2.

primtall = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

Skriver vi så for eksempel

print(primtall[1:9:2])

får vi skrevet ut ei del-liste som består av elementene med indekser 1, 3, 5 og 7 i primtall, det vil si [3, 7, 13, 19].

Skriver vi

print(primtall[-1:-5:-1])

får vi skrevet ut ei del-liste som består av elementene med indekser −1, −2, −3 og −4 i primtall, det vil si [29, 23, 19, 17].

Skriver vi

print(primtall[::2])

får vi skrevet ut ei del-liste som består av elementene med indekser 0, 2, 4, 6 og 8 i primtall, det vil si [2, 5, 11, 17, 23].

Oppgave 1:

Vi oppretter ei liste som heter kvadrat:

kvadrat = [1, 4, 9, 16, 25, 36]

Hva refererer da kvadrat[1:3], kvadrat[:3], kvadrat[2:] og kvadrat[:5:2] til? Skriv først opp svarene dine, og bruk så print() i Python til å sjekke om du har rett.

Se løsningsforslag

Kilder

    • Matthes A. (2019). Python Crash Course. no starch press

Negative indekser i lister

I artikkelen om lister ser vi at lister er variabler som kan inneholde flere elementer, og at vi kan referere til enkeltelementer i ei liste ved hjelp av indekser, der første element i ei liste har indeks 0. Indeksene i ei liste med n elementer vil altså være tallene fra og med 0 til og med n−1. Bruker vi en indeks som er n eller større, får vi feilmeldingen "IndexError: list index out of range".

Antall elementer i ei liste kan vi finne ved hjelp av Python-funksjonen len().

Eksempel 1:

Vi oppretter ei liste som heter primtall, og inneholder de 10 første primtallene:

primtall = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

len(primtall) vil returnere 10.

Indeksene i primtall går da fra og med 0 til og med 9.

Skriver vi så for eksempel

print(primtall[0], primtall[9])

får vi skrevet ut 2 og 29.

Skriver vi

print(primtall[10])

får vi en feilmelding.

Vi kan imidlertid også bruke negative tall som indekser. Negative indekser regner fra høyre mot venstre. Siste element i ei liste har indeks −1, nest siste −2, og så videre. Første element i ei liste med n elementer vil ha indeks −n. Bruker vi en indeks som er mindre enn −n, får vi samme feilmelding som hvis vi bruker en indeks som er n eller større, "IndexError: list index out of range".

Positive indekser i ei liste med n elementer går altså fra 0 til n−1, fra venstre mot høyre, mens negative indekser går fra −1 til −n fra høyre mot venstre.

Eksempel 2:

Vi oppretter ei liste som heter primtall, som i eksempel 1:

primtall = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

Skriver vi så for eksempel

print(primtall[-1], primtall[-10])

får vi skrevet ut 29 og 2.

Oppgave 1:

Vi oppretter ei liste som heter kvadrat:

kvadrat = [1, 4, 9, 16, 25, 36]

Hvilke tall refererer da kvadrat[-2] og kvadrat[-6] til? Skriv først opp svarene dine, og bruk så print() i Python til å sjekke om du har rett.

Se løsningsforslag

Kilder

    • Matthes A. (2019). Python Crash Course. no starch press

Fordeler og ulemper med rekursjon

En stor fordel med rekursjon er at vi kan kode en funksjonsdefinisjon så å si direkte, uten å behøve å tenke på løkker og løkkevariable. Rekursjon gir ofte enkle og elegante løsninger. Sammenlikn for eksempel den iterative og rekursive varianten av fakultetsfunksjonen vist i eksempel 1:

Eksempel 1:

Iterativ versjon:

def fakultet(n):
    """Beregner n! ved iterasjon"""
    if n == 0:
        return 1
    else:
        produkt = 1
        for m in range(1, n + 1):
            produkt *= m
        return produkt

Rekursiv versjon:

def fakultet(n):
    """Beregner n! ved rekursjon"""
    if n == 0:
        return 1
    else:
        return n * fakultet(n - 1)

Av og til kan det faktisk være vanskelig å finne en løsning som ikke er rekursiv. Ulempen med rekursjon er at det er atskillig mindre effektivt enn løkker, fordi å håndtere funksjonskall krever mye ekstra arbeid, såkalt overhead, av datamaskinen. I praksis vil det også finnes en grense for hvor mange funksjonskall vi kan kjede etter hverandre. Går vi over grensa, får vi en feilmelding. I Python ligger denne grensa på rundt 1000.

Av og til kan rekursjon også være en håpløst ineffektiv løsning. Vi skal illustrere dette med en funksjon for å beregne fibonaccitall. Fibonaccitallene er 1, 1, 2, 3, 5, 8, …, en tallfølge der de to første tallene er 1, og hvert tall deretter er lik summen av de to foregående.

En iterativ versjon av en funksjon for å beregne fibonaccitall er vist i eksempel 2.

Eksempel 2:

def fibonacci(n):
    """Beregner fibonaccitall nummer n ved iterasjon."""
    pre_fibo = 1
    fibo = 1
    for _ in range(n - 2):
        fibo, pre_fibo = fibo + pre_fibo, fibo
    return fibo

Hvis vi har funksjonen i eksempel 2, og i hovedprogrammet skriver

for n in range(1, 101):
    print(f"Fibonacctitall {n} er {fibonacci(n)}")

skrives de 100 første fibonaccitallene kjapt ut.

I eksempel 2 representerer fibo fibonaccitallet og pre_fibo tallet før fibonaccitallet. Disse settes i utgangspunktet til 1, siden det første og andre fibonaccitallet er 1. Hvis funksjonen kalles opp med n lik 1 eller 2, gjennomløpes ikke løkka, og fibo beholder verdien 1. Hvis n er 3 eller større, gjennomløpes løkka, der fibo blir satt lik fibo + pre_fibo og pre_fibo blir satt lik fibo.

Det krever imidlertid en del tankearbeid å forstå logikken i eksempel 2. Den rekursive varianten i eksempel 3 er mye enklere å forstå.

Eksempel 3:

def fibonacci(n):
    """Beregner fibonaccitall nummer n ved rekursjon."""
    if n <= 2:
        return 1
    else:
        return fibonacci(n - 1) + fibonacci(n - 2)

I eksempel 3 benytter vi definisjonen av fibonaccitall direkte. Hvis funksjonen kalles opp med n lik 1 eller 2, er returverdien 1. Hvis n er 3 eller større, er returverdien summen av de to foregående fibonaccitallene.

Men hvis vi nå i hovedprogrammet igjen skriver

for n in range(1, 101):
    print(f"Fibonacctitall {n} er {fibonacci(n)}")

ser vi at det er noe som ikke fungerer. Tallene skrives ut langsommere og langsommere, og i nærheten av fibonaccitall 40 stopper det nesten helt opp. Sjekk gjerne ut dette selv.

Grunnen er at vi har to rekursive kall i funksjonen, nemlig fibonacci(n – 1) og fibonacci(n – 2), slik at vi for hvert rekursjonskall genererer to nye, og vi får en eksponentiell vekst i kompleksitet. Samme fibonaccitall beregnes om og om igjen. Trestrukturen under viser hvordan funksjonskallene skjer når vi skal beregne fibonaccitall nummer 6:

Rekursjonstre for fibonaccitall

Vi ser at fibonaccitall nummer 2 beregnes hele 5 ganger.

Rekursjon bør vi generelt ikke bruke hvis et rekursivt kall genererer mer enn ett nytt.

Kilder

    • Matthes A. (2019). Python Crash Course. no starch press