Gå til indholdet

Programflow

Programflow refererer til den rækkefølge, som instruktioner udføres i et program. De fleste programmer følger ikke bare en lige linje fra start til slut - de træffer beslutninger, gentager handlinger og springer rundt baseret på forskellige forhold.

Man kan sige, at programflow handler om spørgsmålet: “Hvad skal programmet gøre som det næste?” Det er netop her, betingelser, løkker og funktioner bliver vigtige.

Sekventiel udførelse

Den simpleste form for programflow er sekventiel udførelse, hvor instruktioner køres én efter én i rækkefølge fra top til bund.

print("Først dette")
print("Så dette")
print("Og til sidst dette")

Mens dette er grundlæggende, er det sjældent nok til at skabe nyttige programmer. Vi har brug for måder at kontrollere flowet på.

I Python er indrykning meget vigtig. Linjer, der står indrykket under en if, for, while eller def, hører til den blok kode, som kun køres i den sammenhæng.

Synkron og asynkron udførelse

Når man taler om programflow, er det også nyttigt at kende forskellen på synkron og asynkron udførelse.

Ved synkron udførelse venter programmet på, at én opgave bliver færdig, før det går videre til den næste. Det er den mest enkle måde at tænke programflow på: først dette, så dette, og derefter det næste.

Ved asynkron udførelse kan programmet sætte en opgave i gang og imens fortsætte med noget andet. Det bruges især, når programmet skal vente på noget langsomt, for eksempel data fra internettet, en database eller en fil.

Et simpelt billede er dette:

  • Synkront: Du står i kø ved en skranke og kan ikke lave andet, før du er færdig.
  • Asynkront: Du tager et nummer, sætter dig og laver noget andet imens, indtil du bliver kaldt op.

Asynkron programmering er især vigtig i webapplikationer, apps og andre programmer, hvor man ikke vil låse hele programmet, mens man venter på svar udefra.

Et vigtigt nøgleord her er await. Når et program møder await, betyder det typisk, at det ikke behøver blokere den tråd eller eksekveringskontekst, der er i gang, mens der ventes på resultatet. I stedet kan runtime-miljøet bruge tiden på noget andet.

Det kan for eksempel være:

  • at en brugerflade stadig kan reagere på klik og tastetryk
  • at en webserver kan håndtere andre forespørgsler imens
  • at programmet kan fortsætte med andre opgaver, mens det venter på netværk, filsystem eller database

Hvornår bruger man hvad?

  • Brug synkron kode, når opgaven er enkel, kort og skal ske i en bestemt rækkefølge.
  • Brug asynkron kode, når programmet ellers ville stå og vente på noget langsomt.

Et vigtigt punkt er dog, at asynkronitet ikke er helt det samme som parallelitet.

  • Asynkronitet handler typisk om ikke at spilde tid, mens programmet venter på noget langsomt.
  • Parallelitet handler om at dele arbejdet op, så flere kerner eller tråde kan arbejde samtidig.

Et godt eksempel er beregning af primtal fra 1 til 4.000.000:

  • Synkront på én kerne: Programmet gennemgår hele intervallet i én lang arbejdsgang. Imens kan den samme arbejdstråd ikke lave andet.
  • Parallelt på fire kerner: Programmet deler arbejdet op i fire områder, for eksempel 1-1.000.000, 1.000.001-2.000.000, 2.000.001-3.000.000 og 3.000.001-4.000.000. Hver kerne kan så arbejde på sin del samtidig.

Hvis opgaven mest handler om tunge beregninger, som i primtal-eksemplet, er parallelitet ofte mere relevant end asynkronitet. Hvis opgaven derimod mest handler om at vente på netværk, filer eller databaser, er asynkronitet ofte den vigtigste teknik.

Man kan derfor sige det sådan:

  • await hjælper især, når programmet venter.
  • Flere kerner hjælper især, når programmet regner.

Hvis et program for eksempel skal hente data fra internettet, giver asynkronitet mening, fordi programmet ellers bare ville stå stille og vente på svar. Hvis programmet derimod skal udføre en tung beregning, som at finde primtal i et meget stort interval, vil man ofte have mere gavn af parallel udførelse på flere kerner.

Betingelser (if-statements)

Betingelser tillader dit program at træffe beslutninger baseret på om noget er sandt eller falsk. Den mest almindelige form er if-sætningen.

En betingelse bygger typisk på en sammenligning, som giver resultatet True eller False. Programmet bruger derefter dette resultat til at vælge, hvilken vej det skal gå.

Simpel if

alder = 20

if alder >= 18:
    print("Du er voksen")

Programmet tjekker om alder er større end eller lig med 18. Hvis ja, udskriver det “Du er voksen”. Hvis nej, sker der ingenting.

if-else

alder = 15

if alder >= 18:
    print("Du er voksen")
else:
    print("Du er mindreårig")

Nu har vi to muligheder: enten er du voksen, eller du er mindreårig.

if-elif-else

karakter = 85

if karakter >= 90:
    print("A")
elif karakter >= 80:
    print("B")
elif karakter >= 70:
    print("C")
elif karakter >= 60:
    print("D")
else:
    print("F")

Med elif (else if) kan vi have mange forskellige grene i vores beslutningslogik.

Indlejrede betingelser

Betingelser kan også indlejres i hinanden:

har_billet = True
er_vip = True

if har_billet:
    if er_vip:
        print("Velkommen til VIP-området")
    else:
        print("Velkommen til general admission")
else:
    print("Du har brug for en billet")

Indlejrede betingelser kan være nyttige, men de kan også hurtigt blive svære at læse. Som tommelfingerregel er det ofte en god idé at holde logikken så enkel som muligt.

Løkker

Løkker tillader dig at gentage kode flere gange. Der er to hovedtyper: for-løkker og while-løkker.

Løkker er vigtige, fordi de gør det muligt at undgå gentagelser i koden. I stedet for at skrive den samme instruktion mange gange, kan du få programmet til at udføre den igen og igen på en kontrolleret måde.

for-løkke

En for-løkke bruges typisk, når du ved, hvor mange gange du vil gentage noget.

# Udskriv tallene 0-4
for i in range(5):
    print(i)

Her ændrer variablen i sig automatisk for hver omgang i løkken. Først er den 0, så 1, så 2 og så videre.

Output:

0
1
2
3
4

Du kan også iterere over en liste:

frugt = ["æble", "banan", "appelsin"]

for f in frugt:
    print(f)

Output:

æble
banan
appelsin

while-løkke

En while-løkke fortsætter så længe en betingelse er sand. Den bruges typisk, når du ikke ved præcis, hvor mange gange løkken skal køre.

tal = 0

while tal < 5:
    print(tal)
    tal = tal + 1

Det afgørende i en while-løkke er, at noget inde i løkken skal ændre betingelsen. Her bliver tal større for hver omgang, og derfor stopper løkken til sidst.

Output:

0
1
2
3
4

Pas på uendelige løkker!

Hvis betingelsen i en while-løkke aldrig bliver falsk, kører løkken for evigt (en uendelig løkke). Dette kan få dit program til at hænge.

# FARLIGT! Uendelig løkke
while True:
    print("Dette stopper aldrig!")

break og continue

Du kan kontrollere løkker yderligere med break og continue:

  • break stopper løkken helt
  • continue springer til næste iteration
# Break eksempel - find første lige tal
for tal in [1, 3, 5, 4, 7, 9]:
    if tal % 2 == 0:
        print(f"Fandt lige tal: {tal}")
        break

# Continue eksempel - udskriv kun ulige tal
for tal in range(10):
    if tal % 2 == 0:
        continue
    print(tal)

Som tommelfingerregel bruges break, når du vil stoppe helt, fordi du har fundet det, du ledte efter. continue bruges, når du bare vil springe én omgang over og fortsætte med resten.

Flowdiagrammer

Flowdiagrammer er en visuel måde at repræsentere programflow på. De bruger standardiserede symboler:

  • Oval: Start/slut
  • Rektangel: Handling/proces
  • Diamant: Beslutning (betingelse)
  • Pile: Flow retning

Simpelt flowdiagram

At tegne et flowdiagram før du skriver kode kan hjælpe dig med at forstå programmets logik og opdage potentielle problemer tidligt.

Funktioner

Funktioner (også kaldet metoder eller procedurer) er navngivne blokke af kode, der udfører en specifik opgave. De er som små programmer inden i dit program.

Funktioner hjælper med at gøre store programmer mere overskuelige. I stedet for at have al logik samlet ét sted, kan du dele problemet op i mindre dele med hvert sit navn og ansvar.

Hvorfor bruge funktioner?

  • Genbrugelighed: Skriv kode én gang, brug den mange gange
  • Organisation: Opdel komplekse problemer i mindre dele
  • Læsbarhed: Giv meningsfulde navne til kodeblokke
  • Vedligeholdelse: Ret fejl ét sted i stedet for mange

Definition og kald

# Definition af en funktion
def sig_hej():
    print("Hej!")

# Kald funktionen
sig_hej()  # Udskriver: Hej!
sig_hej()  # Udskriver: Hej!

Parametre

Funktioner kan modtage input gennem parametre:

def sig_hej_til(navn):
    print(f"Hej, {navn}!")

sig_hej_til("Anna")   # Udskriver: Hej, Anna!
sig_hej_til("Bo")     # Udskriver: Hej, Bo!

Parametre fungerer som variabler, der kun findes inde i funktionen. De får en værdi, når funktionen kaldes.

Returværdier

Funktioner kan også returnere resultater:

def adder(a, b):
    return a + b

resultat = adder(3, 5)
print(resultat)  # Udskriver: 8

Det er ofte nyttigt at tænke sådan: parametre går ind i funktionen, og returværdien kommer ud igen.

Eksempel: Beregn gennemsnit

def beregn_gennemsnit(tal_liste):
    """Beregner gennemsnittet af en liste af tal."""
    if len(tal_liste) == 0:
        return 0

    total = sum(tal_liste)
    antal = len(tal_liste)
    return total / antal

# Brug funktionen
karakterer = [85, 90, 78, 92, 88]
gennemsnit = beregn_gennemsnit(karakterer)
print(f"Gennemsnit: {gennemsnit}")  # Udskriver: Gennemsnit: 86.6

Rekursion

Rekursion er, når en funktion kalder sig selv. Det kan være en elegant måde at løse problemer, der naturligt opdeles i mindre versioner af samme problem.

For mange begyndere er rekursion et af de sværere emner. Det er helt normalt. Du behøver ikke mestre det med det samme for at kunne programmere godt.

def fakultet(n):
    """Beregner n! (fakultet af n)."""
    if n <= 1:
        return 1
    else:
        return n * fakultet(n - 1)

print(fakultet(5))  # 5! = 5*4*3*2*1 = 120

Pas på uendelig rekursion!

Ligesom med løkker skal rekursion have en base case (stopbetingelse), ellers kører den for evigt og forårsager en stack overflow fejl.

Kombination af koncepter

Her er et eksempel, der kombinerer betingelser, løkker og funktioner:

Eksempel på flowdiagram
def er_primtal(tal):
    """Tjekker om et tal er et primtal."""
    if tal < 2:
        return False

    for i in range(2, tal):
        if tal % i == 0:
            return False

    return True

def find_primtal(maksimum):
    """Finder alle primtal op til et givet maksimum."""
    primtal = []

    for tal in range(2, maksimum + 1):
        if er_primtal(tal):
            primtal.append(tal)

    return primtal

# Find alle primtal op til 50
resultater = find_primtal(50)
print(f"Primtal op til 50: {resultater}")

Output:

Primtal op til 50: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]

Næste skridt

Når du forstår programflow, bliver det lettere at tale om, hvordan man løser problemer mere systematisk og effektivt. Derfor er næste naturlige side Algoritmer.