1.3 Løkker: for, while og range
`for`-løkker over sekvenser og `range`, `while`-løkker, nøstede løkker og akkumulatormønsteret — den vanligste kodesporingskilden og motoren i all kodeskriving.
mest brukte konstruksjonen i hele faget. Uten løkke får du ikke skrevet en eneste av funksjonene i Oppgave 3, og ikke en eneste metode i den store Oppgave 4.
I sjanger A — kodeforståelse («hva skriver dette programmet ut?») er løkker den vanligste kilden til feil, og fire feller går igjen år etter år:
- rekkefølgen på oppdateringene inne i løkka — hva har den gamle verdien når
neste regnes ut,
- oppsamling i en nøstet løkke der bare noen av rundene teller,
- en while-løkke som endrer flere variabler per runde,
- av-en-feil i range, fordi stoppverdien ikke er med.
Dette må sitte — bokas høyeste av tre nivåer. Kapitlet er også litt lengre
enn de forrige, så det er lagt opp i fem løkker med tidsanslag. Ta gjerne en pause etter løkke 3.
Forkunnskaper — sist du var her
Kap. 1.1 — Variabler, datatyper og uttrykk og
kap. 1.2 — Betingelser og boolske uttrykk. Du trenger derfra:
- at en tilordning regner ut høyre side først og deretter lar navnet peke på
resultatet — det avgjør alt som skjer inne i en løkke,
- at innrykk bestemmer hvilke linjer som hører til en blokk,
- at en betingelse gir True eller False,
- de sju regneoperatorene, særlig % og //.
Kort oppfrisket, de to tingene du bruker mest her:
total = 0 total = total + 6 if total % 2 == 0:
print("partall", total)Utskrift:
partall 6Legg merke til de to stegene: tilordningen bygger opp en verdi, og betingelsen avgjør om noe skjer. Nettopp de to stegene er byggeklossene i hele dette kapitlet.
Løkke 1 — for og range (~12 min)
En skoleklasse skal betale for en tur. Du vil skrive «Deltaker 1», «Deltaker 2»
og så videre helt til «Deltaker 25». Å skrive 25 print-linjer er utenkelig.
Det du trenger, er en løkke: en blokk med kode som kjøres om og om igjen, med en litt ny verdi hver gang.
En blokk med kode som kjøres flere ganger. Hver gjennomkjøring kalles en
runde eller en iterasjon.
Python har to løkketyper, og de dekker hver sin situasjon:
- for — når du vet hva du skal gå gjennom: alle tallene fra 1 til 25, alle
tegnene i et navn, alle elementene i en samling.
- while — når du ikke vet hvor mange runder det blir, bare når du skal
stoppe: «så lenge saldoen er positiv».
I praksis dekker for de fleste oppgavene på eksamen. while kommer til sin rett når antallet runder avhenger av noe som skjer underveis.
Kjører blokken én gang for hvert element i sekvensen. Variabelen mellom for ogin — løkkevariabelen — får en ny verdi for hver runde:
for tegn in "sol":
print(tegn)Utskrift:
s o lFormkravene er de samme som for if: kolon på slutten av linja, og innrykk på linjene som hører til.
Løkkevariabelen er et vanlig variabelnavn du velger selv. Kall den noe som sier hva den inneholder — tegn når det er tegn, tall når det er tall.
Lager tallrekka fra 0 og oppover, men ikke med stoppverdien:
for i in range(4):
print(i)Utskrift:
0
1
2
3Fire runder, med verdiene 0, 1, 2 og 3. Tallet i parentesen er antallet runder, ikke den siste verdien.
At stoppverdien ikke er med, er den mest omtalte detaljen i hele Python, og årsaken til feilkode #5 — av-med-én. Til gjengjeld gir den en enkel regel:range(N) gir alltid nøyaktig N runder.
Tallrekka fra start til og med stopp minus én:
for i in range(2, 6):
print(i)Utskrift:
2
3
4
5Startverdien er med, stoppverdien er ikke. Antall runder er stopp - start, altså fire her.
Vil du telle fra 1 til 25 slik et menneske ville sagt det, skriver du derforrange(1, 26).
Samme som over, men hopper steg om gangen:
for i in range(0, 10, 3):
print(i)Utskrift:
0
3
6
9Neste verdi ville vært 12, som er forbi stoppverdien, så løkka stopper.
Steget kan være negativt, og da teller løkka nedover. Da må startverdien være den største: range(5, 0, -1) gir 5, 4, 3, 2, 1.
Regelen du skal kunne uten å telle:
| Uttrykk | Verdier | Antall runder |
|---|---|---|
range(4) | 0, 1, 2, 3 | 4 |
range(2, 6) | 2, 3, 4, 5 | 4 |
range(0, 10, 3) | 0, 3, 6, 9 | 4 |
range(0) | ingen | 0 |
range(3, 3) | ingen | 0 |
De to siste er verdt et blikk: en
range der start og stopp er like, gir ingen runder i det hele tatt, og blokken kjøres aldri. Det er ikke en feil — det er ofte akkurat det du vil ha.Skriv ut «Deltaker 1» til «Deltaker 5». Hvilken range trenger du, og hva blir utskriften?
Menneskelig telling begynner på 1 og tar med den siste verdien. range begynner på 0 og tar ikke med stoppverdien. Derfor må begge tallene justeres:
for i in range(1, 6):
print("Deltaker", i)Utskrift:
Deltaker 1
Deltaker 2
Deltaker 3
Deltaker 4
Deltaker 5Fem runder, som er 6 - 1.
Slik regner du det ut på eksamen: ta stoppverdien minus startverdien. Vil du
ha verdiene 1 til og med 25, skriver du range(1, 26) og får 25 runder.
Legg merke til at print("Deltaker", i) setter ett mellomrom mellom teksten og tallet, slik du så i kap. 1.1. Her slipper du å konvertere tallet, fordi komma i print tåler alle typer.
(Innstegsoppgave — ren gjengivelse.) Svar uten å skrive kode.
a) Hvor mange runder gir range(7), og hva er første og siste verdi?
b) Hvor mange runder gir range(3, 9)?
c) Hvilke verdier gir range(0, 8, 2)?
d) Hvor mange runder gir range(5, 5)?
Løkke 2 — Teller og akkumulator (~12 min)
En løkke som bare skriver ut, er sjelden nok. Som regel skal du samle opp noe underveis: hvor mange var det, hva ble summen, hvilken var størst.
To mønstre dekker nesten alt dette, og de er de to første av bokas elleve kodemønstre. De kommer igjen i hver eneste funksjon du skriver resten av faget.
Å bygge opp en verdi litt etter litt gjennom en løkke. Tre faste steg:
1. Før løkka: lag variabelen og gi den en startverdi, som regel 0.
2. Inne i løkka: oppdater den med den nye verdien.
3. Etter løkka: bruk resultatet.
total = 0 for i in range(1, 5):
total = total + i print(total)Utskrift:
10Summen av 1, 2, 3 og 4.
Det avgjørende er hvor de tre stegene står. Står total = 0 inne i løkka,
nullstilles den hver runde og svaret blir bare det siste tallet. Står print inne i løkka, får du fire utskrifter i stedet for én. Innrykket er hele forskjellen.
Å telle hvor mange ganger noe skjer. Samme tre steg som akkumulatoren, men du legger alltid til 1, og bare når betingelsen er sann:
antall = 0 for i in range(1, 11):
if i % 3 == 0:
antall = antall + 1 print(antall)Utskrift:
3Tallene 3, 6 og 9 er delelige med 3.
Legg merke til de to nivåene av innrykk: if hører til løkka, og oppdateringen hører til if. Det er dette mønsteret alle «hvor mange
…»-oppgavene på eksamen bruker, og du kan skrive antall += 1 i stedet hvis du vil.
Gå gjennom tallene fra 1 til 6, og finn både summen av alle og antallet partall. Hvorfor kan begge deler gjøres i samme løkke?
Du trenger to variabler før løkka, og de oppdateres uavhengig av hverandre inne i den:
total = 0 antall_partall = 0 for i in range(1, 7):
total = total + i if i % 2 == 0:
antall_partall = antall_partall + 1 print(total, antall_partall)Utskrift:
21 3Summen 1 + 2 + 3 + 4 + 5 + 6 er 21, og partallene er 2, 4 og 6.
Tavla, runde for runde:
i | total etter | Partall? | antall_partall etter |
|---|---|---|---|
| 1 | 1 | nei | 0 |
| 2 | 3 | ja | 1 |
| 3 | 6 | nei | 1 |
| 4 | 10 | ja | 2 |
| 5 | 15 | nei | 2 |
| 6 | 21 | ja | 3 |
Hvorfor det virker: de to variablene lever hver sitt liv.
total oppdatereshver runde fordi linja står rett under
for; antall_partall oppdateres bare når if-en slår til, fordi linja står under if.Å samle flere ting i én løkke er den vanlige formen på eksamen — det er sjelden verdt å gå gjennom de samme dataene to ganger.
Hva skriver programmet ut?
total = 0 for i in range(4):
total = total + i * 2 print(total)Skriv et program som teller hvor mange av tallene fra 1 til 20 som er delelige med 4, og skriver ut antallet.
a) Skriv koden.
b) Hva ville skjedd om du satte antall = 0 inne i løkka?
c) Hva ville skjedd om print sto inne i løkka?
Løkke 3 — while (~10 min)
Noen ganger vet du ikke hvor mange runder du trenger. «Halver tallet til det er mindre enn 1.» «Fortsett så lenge saldoen holder.»
Da bruker du en while-løkke, som kjører så lenge en betingelse er sann.
— naturlig pausepunkt —
Kjører blokken om og om igjen så lenge betingelsen er sann. Betingelsen sjekkes
før hver runde:
n = 8 while n > 1:
print(n)
n = n // 2 print("ferdig")Utskrift:
8
4
2 ferdigLegg merke til at 1 aldri skrives ut: når n er blitt 1, er betingelsen1 > 1 usann, og løkka avsluttes før neste runde.
Er betingelsen usann allerede første gang, kjøres blokken null ganger. Enwhile-løkke er altså ikke garantert å kjøre i det hele tatt.
En while-løkke stopper bare hvis noe inne i den gjør betingelsen usann til slutt. Linja som gjør det, er like viktig som selve betingelsen.
I eksempelet over er det n = n // 2. Uten den ville n vært 8 for alltid, og8 > 1 ville vært sant for alltid.
Vanen som redder deg: når du har skrevet while, finn med én gang linja som
til slutt gjør betingelsen usann, og skriv den før du skriver resten av kroppen.
En while-løkke der betingelsen aldri blir usann, stopper aldri. Programmet henger, og på en maskin må du avbryte det manuelt.
n = 8 while n > 1:
print(n)Denne blokken har ingen linje som endrer n, så den skriver 8 i det uendelige.
Den er merket som ikke-kjørbar nettopp fordi den aldri ville stoppet — all annen kode i boka er faktisk kjørt, og utskriften er den terminalen ga.
På eksamen er dette først og fremst en sporingsfelle: du blir bedt om å angi hva et program skriver ut, og riktig svar kan være at det aldri blir ferdig.
Sjekk alltid at noe inne i løkka påvirker betingelsen.
Den motsatte fella er en while der betingelsen er usann fra start. Da skrives ingenting, og svaret er «ingen utskrift».
Dette er en klassisk sporingsoppgave. Hva skriver programmet ut, og hvorfor er rekkefølgen på de to siste linjene i løkka avgjørende?
n = 20 s = 0 while n > 1:
s = s + n n = n // 2 print(s, n)Før en tavle. Sjekk betingelsen før hver runde, og oppdater i den rekkefølgen linjene står.
| Runde | n ved start | n > 1? | s etter | n etter |
|---|---|---|---|---|
| 1 | 20 | ja | 20 | 10 |
| 2 | 10 | ja | 30 | 5 |
| 3 | 5 | ja | 35 | 2 |
| 4 | 2 | ja | 37 | 1 |
| — | 1 | nei | 37 | 1 |
Utskrift:
37 1Hvorfor rekkefølgen avgjør: i hver runde legges den gamle verdien av ntil summen, før
n halveres. Byttet du om de to linjene, ville den halverte verdien blitt lagt til i stedet, og svaret blitt 10 + 5 + 2 + 1, altså 18.Legg også merke til at
n er 1 etter løkka, ikke 0. Løkkevariabelen i enwhile beholder verdien den hadde da betingelsen sviktet, og den verdien kan du bli spurt om.Hva skriver programmet ut?
tall = 1 runder = 0 while tall < 50:
tall = tall * 3 runder = runder + 1 print(tall, runder)Løkke 4 — Nøstede løkker (~10 min)
En løkke kan stå inne i en annen. Da kjøres hele den indre løkka på nytt for hver runde i den ytre — og antall runder ganges sammen.
Nøstede løkker er den tyngste sporingssjangeren i Oppgave 1, og de er verdt å øve på til de er mekaniske.
En løkke inne i en annen løkke. Den indre kjøres helt ferdig for hver eneste runde i den ytre:
for i in range(2):
for j in range(3):
print(i, j)Utskrift:
0 0
0 1
0 2
1 0
1 1
1 2Seks linjer: to ytre runder ganger tre indre.
Regelen for sporing: den indre løkka begynner forfra hver gang. Legg merke
til at j starter på 0 igjen når i blir 1.
Med faste grenser er antallet linjer alltid produktet av de to.
Den indre range kan bruke den ytre løkkevariabelen. Da blir antall indre runder forskjellig for hver ytre runde:
for i in range(4):
for j in range(i):
print(i, j)Utskrift:
1 0
2 0
2 1
3 0
3 1
3 2Seks linjer, ikke seksten. Når i er 0, gir range(0) ingen runder i det hele tatt, og den indre løkka kjøres aldri.
Antallet blir 0 + 1 + 2 + 3, altså 6. Dette mønsteret er en fast kilde til sporingsoppgaver nettopp fordi den første ytre runden ikke gir noe utslag.
Tre varianter av samme feil, og alle tre er testet på eksamen:
- Glemme at stoppverdien ikke er med. range(4) gir 0, 1, 2, 3 — ikke 1 til
4, og ikke 0 til 4.
- Feil grense i den indre løkka. range(i) gir i runder, mens `range(i +
1)` gir én til. Forskjellen er én linje utskrift per ytre runde.
- Telle antall runder feil. Antallet er alltid stopp - start, aldri
stopp - start + 1.
Motmiddelet er alltid det samme, og det tar ti sekunder: skriv opp verdienerange faktisk gir, før du sporer noe som helst. range(2, 6) er 2, 3, 4, 5
— fire tall.
Er du usikker på en nøstet løkke, spor de to første ytre rundene helt ut. Da ser du mønsteret, og resten kan du regne.
Dette er den vanskeligste sporingsformen i Oppgave 1: nøstet løkke, betingelse og akkumulator i samme program. Hva skriver det ut?
total = 0 for i in range(4):
for j in range(i):
if j % 2 == 0:
total = total + 1 print(total)Bygg tavla én ytre runde om gangen, og noter hvilke j-verdier som finnes.
i | range(i) gir | j-verdier med j % 2 == 0 | total etter |
|---|---|---|---|
| 0 | ingenting | — | 0 |
| 1 | 0 | 0 | 1 |
| 2 | 0, 1 | 0 | 2 |
| 3 | 0, 1, 2 | 0, 2 | 4 |
Utskrift:
4Tre observasjoner som er verdt poeng:- Den første ytre runden gir ingen indre runder, fordi
range(0) er tom.- Betingelsen filtrerer bort omtrent halvparten av de indre rundene, så antall
oppdateringer er langt lavere enn antall indre runder (som er 0 + 1 + 2 + 3 =
6).
- total overlever mellom de ytre rundene, fordi den er laget før begge
løkkene.
Slik ville sensor sett på det: en kandidat som setter opp riktig tavle men
regner feil på én rad, har vist hele metoden og får god uttelling. Den som svarer 6, har glemt betingelsen; den som svarer 10, har regnet med atrange(i) gir i + 1 runder.
Hva skriver programmet ut? Skriv utskriften nøyaktig, med linjeskift.
for i in range(1, 4):
for j in range(2):
print(i * j)Hva skriver programmet ut?
total = 0 for i in range(5):
for j in range(i):
total = total + 1 print(total)Løkke 5 — Rekkefølgen på oppdateringene (~6 min)
Den siste fella i kapitlet er også den mest omtalte i eksamensarkivet, og den handler ikke om løkker i det hele tatt — den handler om tilordning.
Når en løkke oppdaterer to variabler i samme runde, og den ene bruker den andre, avgjør rekkefølgen på linjene hva svaret blir.
Inne i en løkke leses høyre side av en tilordning med de verdiene variablene har
akkurat nå — altså etter alle tidligere linjer i samme runde.
a = 1 b = 2 for i in range(3):
a = 2 * a + b b = b + 1 print(a, b)Utskrift:
26 5Merk at a bruker den gamle b i sin egen linje, fordi b oppdateres etterpå. Byttet du om de to linjene, ville a brukt den nye b, og svaret blitt et helt annet.
Dette er ikke en spissfindighet: det er et fast innslag i Oppgave 1, og fasiten avhenger av at du sporer linjene i den rekkefølgen de står.
De to programmene har de samme fire linjene — bare rekkefølgen inne i løkka er byttet. Hva skriver hvert av dem ut?
a = 1 b = 2 for i in range(3):
a = 2 * a + b b = b + 1 print("A:", a, b)a = 1 b = 2 for i in range(3):
b = b + 1 a = 2 * a + b print("B:", a, b)a regnes først og bruker den gamle b:| Runde | a før | b før | a etter | b etter |
|---|---|---|---|---|
| 1 | 1 | 2 | 4 | 3 |
| 2 | 4 | 3 | 11 | 4 |
| 3 | 11 | 4 | 26 | 5 |
Utskrift:
A: 26 5Andre program, der b økes først og a dermed bruker den nye verdien:| Runde | a før | b etter økning | a etter |
|---|---|---|---|
| 1 | 1 | 3 | 5 |
| 2 | 5 | 4 | 14 |
| 3 | 14 | 5 | 33 |
Utskrift:
B: 33 5Samme startverdier, samme antall runder, samme regnestykke — og likevel 26 mot
33. Forskjellen er utelukkende hvilken linje som står først.
Slik sporer du det trygt: skriv én kolonne per variabel, og fyll ut radene i
den rekkefølgen linjene står. Aldri regn to oppdateringer «samtidig».
Hva skriver programmet ut?
x = 3 y = 1 for i in range(3):
x = x + y y = x - y print(x, y)Hva skriver programmet ut? Skriv utskriften nøyaktig, med linjeskift.
antall = 0 n = 30 while n > 1:
if n % 2 == 0:
n = n // 2 else:
n = n - 1 antall = antall + 1 print(n, antall)Fem feller står for nesten alle poengene som mistes på løkker:
- #5 — av-med-én. range(4) gir 0, 1, 2, 3. Stoppverdien er aldri med, og
antall runder er stopp - start.
- Muteringsrekkefølge. Når to variabler oppdateres i samme runde, bruker den
andre linja den nye verdien fra den første.
- Innrykk på feil nivå. En startverdi inne i løkka nullstilles hver runde;
en print inne i løkka gir én linje per runde. Innrykket er betydning.
- Glemt å endre betingelsesvariabelen i en while. Da blir løkka uendelig,
og riktig svar på sporingsoppgaven er at programmet aldri blir ferdig.
- Tom indre løkke oversett. range(0) og range(3, 3) gir null runder, og
blokken kjøres aldri.
Og én lesefeil: å tro at betingelsen i en while sjekkes etter runden. Den sjekkes før, og derfor kan sluttverdien godt ligge langt forbi grensen.
Begrepsbank
Begrepsbanken er flashcard- og repetisjonsstoff — den gjentar det du nettopp har lest. Hopp trygt over ved førstegangslesing; tidsanslaget for kapitlet gjelder kjernestoffet.
Én gjennomkjøring av løkkekroppen. En løkke med fire iterasjoner kjører blokken fire ganger.
Ordet brukes også som verb: «å iterere over noe» betyr å gå gjennom det element for element. Du møter det i oppgavetekster og i fasiter, og det betyr aldri noe mer avansert enn dette.
Variabelen som får en ny verdi for hver runde — i i for i in range(5):.
To ting om den. Den lever videre etter løkka, med verdien fra siste runde, og den verdien kan du bli spurt om. Og navnet er ditt eget valg: bruk i for indekser og tellevariabler, og et beskrivende navn ellers.
Kjørte løkka null ganger, finnes variabelen ikke i det hele tatt.
Linjene som kjøres hver runde, altså de innrykkede linjene under for ellerwhile.
Å avgjøre hva som er i kroppen og hva som er utenfor, er første steg i enhver sporing. Startverdier hører utenfor, foran løkka; sluttresultatet skrives ut utenfor, etter løkka.
range(start, stopp, steg) med negativt steg teller nedover:range(5, 0, -1) gir 5, 4, 3, 2, 1.Reglene er de samme som ellers: startverdien er med, stoppverdien er ikke. Med negativt steg må startverdien være størst, ellers blir rekka tom og blokken kjøres aldri.
for tegn in tekst: går gjennom strengen ett tegn om gangen, fra venstre mot høyre. Løkkevariabelen holder på ett enkelt tegn — altså en streng av lengde én.Antall runder er antall tegn i teksten, mellomrom medregnet. En tom tekst gir null runder.
Dette er inngangen til alle streng-oppgavene i
kap. 3.3, der du bygger opp en ny tekst tegn for tegn.
En sekvens uten elementer: range(0), range(3, 3), eller en tom tekst.
En for-løkke over en tom sekvens kjører blokken null ganger. Det er ikke en feil, og det gir ingen feilmelding — programmet går rett videre til linja etter løkka.
I en sporingsoppgave er dette et fullgodt svar: «løkka kjører aldri, så programmet skriver ingenting».
En while-løkke der betingelsen aldri blir usann. Programmet stopper aldri av seg selv.
Årsaken er nesten alltid den samme: ingen linje inne i løkka endrer variabelen betingelsen spør om. Motmiddelet er å skrive den linja først, før resten av kroppen.
I en sporingsoppgave kan riktig svar være at programmet aldri blir ferdig. Se derfor alltid etter linja som til slutt gjør betingelsen usann.
En tabell der du skriver én rad per runde og én kolonne per variabel som endrer seg, pluss en kolonne for hva som er skrevet ut så langt.
Tavla er bokas standardverktøy for sjanger A, og den er grunnen til at du kan løse en nøstet løkke uten å gjette. Den koster et halvt minutt på eksamen og fjerner nesten hele feilrisikoen.
Metoden står i sin fulle form i kap. 4.1.
Med faste grenser ganges antallet: en ytre løkke med 3 runder og en indre med 2 gir 6 gjennomkjøringer av den indre blokken.
Med en indre grense som avhenger av den ytre må du summere i stedet. for i in range(4): for j in range(i): gir 0 + 1 + 2 + 3, altså 6 — ikke 16.
Regn alltid ut antallet før du sporer. Da vet du hvor mange linjer utskrift du skal ende opp med, og du oppdager med én gang om sporingen sklir ut.
Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.
Skolesaga er en uavhengig læringsressurs og er ikke tilknyttet eller godkjent av Universitetet i Oslo. Dette er ikke offisielt studiemateriell. Les mer.