1.7 DRILL — Kodesporing: «hva skrives ut?»
Systematisk drill i å kjøre kode mekanisk i hodet og angi utskriften nøyaktig — sjangeren som bærer hele midtveis.
- Sjanger A — kodesporing, altså «hva skriver dette programmet ut?». Den forekommer i 100 % av settene (8 av 8 avsluttende sett 2018–2025) og utgjør 10–12 av de 20 spørsmålene på midtveiseksamen — UiOs digitale flervalgseksamen i oktober, som teller 25 % av karakteren. På den avsluttende eksamenen i desember er den de første oppgavene, 1–3 poeng hver.
- Sjanger B — matche-tabell, der du får en liste med indekserings- og utsnittsuttrykk og skal parre hvert av dem med riktig verdi. Den er et fast innslag, og gir 0,5 poeng per rad, opptil 3,5 poeng.
- Sjanger D — matche-tabell, der du avgjør om en løkke stopper eller går i det uendelige, eller hvilke av flere kodealternativer som er riktige. Den kommer jevnlig, 2–3,5 poeng.
Til sammen er dette de billigste poengene i faget: de krever ingen ny teori, bare at du kan kjøre koden mekanisk i hodet uten å ta snarveier.
Prioritet: dette må sitte (høyeste prioritet av bokas tre nivåer — «må sitte», «må kunne», «bør kjenne til»). Du får delvis uttelling for delvis riktig sporing på fritekstoppgavene, og på flervalg gir blankt og feil likt — så kryss alltid av.
- Kap. 1.1 — Variabler, datatyper, uttrykk og operatorpresedens — //, %, ** og presedens.
- Kap. 1.2 — Lister, indeksering og slicing — indeksering, negative indekser og utsnitt.
- Kap. 1.3 — Løkker — for, while, range og nøstede løkker — range, nøstede løkker, enumerate, zip og terminering.
- Kap. 1.4 — Strenger, f-strenger og formatert utskrift — end, sep, bredde og desimaler.
- Kap. 1.5 — Dictionaries og nøstede dictionaries — oppslag og løkker over ordbøker.
- Kap. 1.6 — Funksjoner, scope og lambda — scope og returverdier.
Kapitlet er langt (~90 minutter) og er delt i tre bolker med pausepunkter. Ta gjerne én bolk per økt — det er drill, ikke ny teori.
Bolk 1 — Sporingsmetoden og det gjennomarbeidede caset (~20 min)
Å spore kode er ikke å lese den. Det er å kjøre den, med blyant, ett skritt om gangen. De fleste feil på midtveis kommer av at man leste hva programmet så ut som det gjorde, i stedet for å regne ut hva det faktisk gjør.
Oppskriften under er bokas metode, og den brukes i hver eneste fasit i dette kapitlet.
1. Før en tavle over variablene. Skriv navnene øverst, én kolonne per variabel som endrer seg, og én rad per linje eller per iterasjon. Legg til en siste kolonne: «utskrift så langt».
2. Tell løkkerundene før du begynner. range(a, b) gir b - a runder, og stoppverdien er aldri med. Nøstede løkker telles innenfra: for hver runde i den ytre, hvor mange runder går den indre?
3. Noter hva hvert print faktisk skriver. Ikke bare verdiene — også mellomrommene print setter mellom argumentene, effekten av end og sep, og bredden og antall desimaler i en f-streng.
4. Hold styr på om du har et element eller en struktur. x[1] gir et element, x[1:3] gir en liste. En liste skrives med hakeparenteser og , , en tuppel med parenteser, en ordbok med krøllparenteser og kolon.
5. Skriv svaret tegn for tegn. Til slutt setter du sammen utskriften linje for linje, med de mellomrommene og linjeskiftene du noterte i steg 3. Et svar med riktige tall og feil skilletegn er feil svar.
Metoden tar 60–90 sekunder per oppgave når den sitter. Det er raskt nok til de 20 midtveisspørsmålene, og den er den eneste måten å svare presist på en oppgave som ber om utskriften «nøyaktig slik terminalen viser den».
Et program behandler fire målinger fra tre stasjoner. Skriv utskriften nøyaktig, med mellomrom og linjeskift slik terminalen viser den.
maalinger = [3, 8, 2, 9]
navn = ['nord', 'sor', 'ost']
total = 0
for i, v in enumerate(maalinger):
if v > 4:
total += v
print(i, total, end='; ')
print()
for n, v in zip(navn, maalinger):
print(f'{n:5s}{v:3d}')
print(maalinger[1:3], maalinger[-1])enumerate gir (indeks, verdi), i den rekkefølgen. Lista har fire elementer, så løkka går fire runder. Merk at print står utenfor if-en, med mindre innrykk, og derfor kjører hver runde.| Runde | i | v | v > 4? | total etter | Skriver |
|---|---|---|---|---|---|
| 1 | 0 | 3 | nei | 0 | 0 0; |
| 2 | 1 | 8 | ja | 8 | 1 8; |
| 3 | 2 | 2 | nei | 8 | 2 8; |
| 4 | 3 | 9 | ja | 17 | 3 17; |
Margnotat: her ligger to poeng. Det ene er at
total bare oppdateres i to av fire runder; det andre er at end='; ' fjerner linjeskiftet, så alle fire rundene havner på samme linje. print() uten argumenter etterpå avslutter den linja.Steg 3: den andre løkka.
zip stopper ved den korteste — navn har tre elementer, maalinger fire — så løkka går tre runder, og tallet 9 blir aldri brukt her. F-strengen gir navnet bredde 5 (venstrejustert, som strenger alltid er) og tallet bredde 3 (høyrejustert, som tall alltid er).| Runde | n | v | Skriver |
|---|---|---|---|
| 1 | 'nord' | 3 | nord 3 |
| 2 | 'sor' | 8 | sor 8 |
| 3 | 'ost' | 2 | ost 2 |
Margnotat:
'nord' er fire tegn og fylles ut til fem, så det blir ett mellomrom etter navnet pluss to fra tallets bredde — til sammen tre. 'sor' er tre tegn og får fire mellomrom. Å telle disse riktig er forskjellen på full og halv uttelling.Steg 4: siste linje.
maalinger[1:3] er et utsnitt og gir en liste med elementene på plass 1 og 2 — stoppverdien 3 er ikke med. maalinger[-1] er en indeks og gir elementet 9. print med to argumenter setter ett mellomrom mellom.Steg 5: utskriften, tegn for tegn.
Utskrift:
0 0; 1 8; 2 8; 3 17;
nord 3
sor 8
ost 2
[8, 2] 9Legg merke til at den første linja slutter med ; — også det siste skilletegnet skrives, fordi end gjelder hver gang.Uttelling: på en fritekstoppgave gis poeng per riktig linje. Får du strukturen riktig — fire par på linje 1, tre navnelinjer, en sisterad med liste og tall — teller det tungt selv om du skulle bomme på ett mellomrom. Skriv derfor alltid ned det du har, også når du er usikker på detaljene.
— naturlig pausepunkt —
Bolk 2 — Fellekatalogen: de ti fellene som går igjen (~25 min)
Kodesporingsoppgavene er ikke tilfeldige. De er bygget rundt et lite antall feller, og de samme ti går igjen år etter år. Under står de hver for seg, med et minimalt eksempel og den kjørte utskriften. Lærer du deg å kjenne igjen hvilken felle en oppgave sikter mot, vet du også hva du skal sjekke to ganger.
range(b) starter på 0 og stopper før b. range(a, b) gir b - a verdier. Stoppverdien er aldri med.for i in range(3):
print(i, end=' ')
print()
for i in range(1, 4):
print(i, end=' ')Utskrift:
0 1 2
1 2 3 Begge løkkene går tre runder, men med ulike verdier. Dette er feilkode #1 i bokas feilkatalog — av-én-feilen — og den hyppigste feilen i hele faget. Regelen å huske: n intervaller krever n + 1 punkter, altså range(n + 1).
x[i] gir elementet; x[a:b] gir en liste med elementene fra og med a til, men ikke med, b. Negative indekser teller fra -1 bakerst.x = [10, 20, 30, 40]
print(x[1], x[-1])
print(x[1:3], x[:2], x[::-1])Utskrift:
20 40
[20, 30] [10, 20] [40, 30, 20, 10]Feilkode #18. To detaljer avgjør poenget: at utsnittet skrives med hakeparenteser selv når det bare inneholder ett element, og at et utsnitt utenfor kanten gir en tom liste [] i stedet for en feilmelding.
På lister betyr + skjøting og * gjentakelse. Elementvis addisjon finnes ikke for lister — den kommer først med NumPy i kap. 4.1.
print([1, 2] + [3, 4])
print([1, 2] * 2)
print(sum([1, 2]) + sum([3, 4]))Utskrift:
[1, 2, 3, 4]
[1, 2, 1, 2]
10Feilkode #5. Merk at bare siste linje gir et tall — de to første gir lister, og skrives med hakeparenteser.
print setter ett mellomrom mellom argumentene og avslutter med linjeskift. end= bytter ut linjeskiftet, sep= bytter ut mellomrommet.print(3, 4)
print(3, 4, sep='')
print('a', end='')
print('b')
print([1, 2])Utskrift:
3 4
34
ab
[1, 2]Feilkode #16. Fem print ga fire linjer, fordi den tredje ikke avsluttet sin. Og legg merke til at en liste skrives med mellomrom etter kommaet: [1, 2], ikke [1,2].
zip(a, b) parer posisjon for posisjon og stopper ved den korteste; den fyller ikke ut. enumerate(seq) gir (indeks, verdi) — alltid i den rekkefølgen.a = [1, 2, 3]
b = ['x', 'y']
for p, q in zip(a, b):
print(p, q, end=' | ')
print()
for i, v in enumerate(b):
print(i, v, end=' | ')Utskrift:
1 x | 2 y |
0 x | 1 y | Feilkode #17. Den første løkka går to runder, ikke tre — tallet 3 blir aldri brukt. Den andre begynner på indeks 0, ikke 1.
Når den indre løkkas grense avhenger av den ytre løkkevariabelen, må du telle rundene på nytt for hver ytre runde. Og range(0) er tom — den indre løkka kjører da ingen ganger.
for i in range(1, 4):
for j in range(i):
print(i * j, end=' ')
print()Utskrift:
0
0 2
0 3 6 Feilkode #12. For i = 1 går den indre løkka én runde med j = 0; for i = 2 to runder; for i = 3 tre. Legg merke til at print() står i den ytre løkka, med samme innrykk som den indre for-linja — derfor ett linjeskift per ytre runde.
En while-løkke går så lenge betingelsen er sann. Spørsmålet «stopper den?» besvares ved å se om verdien beveger seg mot betingelsen — og om den treffer den.
n = 10
antall = 0
while n > 1:
n = n - 3
antall += 1
print(n, antall)Utskrift:
1 3n går 10, 7, 4, 1 — og ved 1 er n > 1 usann, så løkka stopper etter tre runder. Hadde betingelsen vært n != 1 med steget -4, ville n gått 10, 6, 2, −2, … og aldri truffet 1: uendelig løkke. Det er nøyaktig det sjanger D spør om.
Et likhetstegn inne i en funksjon lager alltid en ny, lokal variabel.
x = 5
def f():
x = 10
return x
print(f(), x)Utskrift:
10 5Feilkode #13. Unntaket å huske: liste.append(...) og d[nøkkel] = ... endrer innholdet i noe som ble sendt inn, og det ser du utenfor. Skillet er mellom å bytte ut et navn og å endre et objekt — se kap. 1.6.
/ gir alltid flyttall, // runder nedover (også for negative tall), og ** regnes fra høyre.print(7 // 2, -7 // 2, 7 / 2)
print(2 ** 3 ** 2)
print(int(-2.7), round(2.5))Utskrift:
3 -4 3.5
512
-2 2Feilkode #15. Tre feller på tre linjer: -7 // 2 er -4 og ikke -3; 2 ** 3 ** 2 er og ikke ; int kutter mot null mens round runder til nærmeste partall ved 5. Og husk at 3.5 og 3 er to forskjellige utskrifter.
for k in d gir nøklene, og in og len ser bare på nøklene, på det nivået du peker på.d = {'a': 1, 'b': 2}
for k in d:
print(k, d[k], end='; ')
print()
print(len(d), 'a' in d, 1 in d)Utskrift:
a 1; b 2;
2 True FalseSiste verdi er fellen: tallet 1 er en verdi i ordboka, ikke en nøkkel, så svaret er False. Parene kommer i innsettingsrekkefølge — se kap. 1.5.
- Å lese i stedet for å regne. «Denne løkka summerer jo lista» — kanskje, men range-grensene avgjør hvor mange ledd som faktisk kommer med. Tell dem.
- Å svare med tallene, men ikke med tegnene. [8, 2] 9 og 8 2 9 er ikke samme svar. Hakeparenteser, komma-mellomrom, fnutter og desimaler er en del av utskriften.
- Å hoppe over de siste linjene. Mange sporinger har en avsluttende linje etter løkka, som tester noe helt annet enn løkka gjorde. Den er ofte det halve poenget.
— naturlig pausepunkt —
Bolk 3 — Drilloppgavene (~45 min)
Fjorten oppgaver på eksamensnivå, alle med fullstendig sporing i fasiten. De roterer gjennom fellene over og gjennom de tre sjangrene: rene «hva skrives ut» (A), matche-tabeller for indeksering og utsnitt (B), og «terminerer løkken?» (D).
Gjør dem med blyant og uten maskin. Eksamen er hjelpemiddelfri, og hele poenget med sporing er at du skal klare den i hodet.
(Innstegsoppgave, sjanger A — kodesporing, altså «hva skriver programmet ut?».) Skriv utskriften nøyaktig, med mellomrom slik terminalen viser den.
total = 0
for i in range(4):
total += i
print(total, end=' ')(Midtveisnivå, sjanger A — nøstet løkke.) Hva skriver programmet ut?
for i in range(2, 5):
for j in range(i - 1):
print(i + j, end=' ')
print('|', end=' ')Dette er den mest utbredte nøstede-løkke-fellen på midtveis: den indre grensen er et uttrykk i den ytre løkkevariabelen. Tre spørsmål avgjør svaret hver gang: hvor mange ytre runder, hvor mange indre runder per ytre runde, og hva som faktisk skrives.
En trekantet utskrift som denne er alltid et signal om at range(i) eller range(i - 1) er i sving.
(Midtveisnivå, sjanger A — strenger som sekvenser.) Hva skriver programmet ut? Skriv utskriften nøyaktig, linje for linje.
navn = 'Blindern'
print(navn[0], navn[-2], len(navn))
print(navn[2:5], navn[:3] + navn[-1])(Midtveisnivå, sjanger A — formatert utskrift.) Hva skriver programmet ut? Skriv utskriften nøyaktig, med mellomrom.
for k in range(3):
print(f'{k:2d}:{2 ** k:6.2f}', end=' # ')(Midtveisnivå, sjanger B — matche-tabell der du regner ut indekserings- og utsnittsuttrykk og parer dem med riktig verdi.) La
x = [4, 1, 3]
y = [[9, 8], [7, 6, 5], [2]]| Rad | Uttrykk |
|---|---|
| 1 | y[x[1]] |
| 2 | y[x[0] - 2][x[2]:] |
| 3 | x[-1] + x[x[1]] |
| 4 | y[0] + y[2] |
Alternativer: (i)
4, (ii) [], (iii) [9, 8, 2], (iv) [7, 6, 5], (v) [2, 9, 8].(Midtveisnivå, sjanger A — liste-skjøting mot tallsum.) Hva skriver programmet ut?
a = [1, 2]
b = [3, 4]
print(a + b, len(a + b))
print(sum(a) + sum(b))
print(a * 2)(Midtveisnivå, sjanger A — zip og enumerate.) Hva skriver programmet ut? Skriv utskriften nøyaktig, linje for linje.
dager = ['man', 'tir', 'ons', 'tor']
mm = [4, 0, 12]
for d, v in zip(dager, mm):
print(d, v, end='; ')
print()
for i, d in enumerate(dager):
if i % 2 == 0:
print(i, d, end='; ')(Midtveisnivå, sjanger D — matche-tabell der du avgjør om løkka stopper.) Avgjør for hver av de fire løkkene om den terminerer eller går uendelig. Alle starter med de verdiene som står.
| Rad | Løkke |
|---|---|
| 1 | n = 12 og while n != 0: n = n - 4 |
| 2 | n = 12 og while n != 0: n = n - 5 |
| 3 | n = 1 og while n < 100: n = n * 3 |
| 4 | n = 5 og while n > 0: n = n + 1 |
Alternativer: (i) terminerer, (ii) uendelig.
(Midtveisnivå, sjanger A — scope og muterbare argumenter.) Hva skriver programmet ut? Skriv utskriften nøyaktig, linje for linje.
grense = 4
def sjekk(verdi):
grense = 100
return verdi > grense
print(sjekk(10), grense)
verdier = [1, 2]
def utvid(liste):
liste.append(9)
liste = [0]
return liste
print(utvid(verdier), verdier)Dette er scope-spørsmålet i sin vanskeligste form: begge reglene i samme program. Sensor plasserer dem ofte slik at det ene svaret ser ut til å motsi det andre.
Regelen som løser alle varianter: et likhetstegn peker et navn mot noe nytt og virker bare lokalt; et metodekall eller en indeksert innsetting endrer objektet, og virker overalt der objektet er kjent.
Hva skriver programmet ut? Skriv utskriften nøyaktig, linje for linje.
d = {}
for tegn in 'banan':
d[tegn] = d.get(tegn, 0) + 1
print(d)
for k, v in d.items():
print(k * v, end='-')En målelinje skal deles opp. Hva skriver programmet ut? Skriv utskriften nøyaktig, linje for linje.
tekst = 'temp=21.5;vind=3'
felt = tekst.split(';')
print(felt)
forste = felt[0].split('=')
print(forste[1], float(forste[1]) + 1)
print(f'{float(forste[1]):7.3f}|')La
rader = [[1, 2, 3], [4, 5], [6]]| Rad | Uttrykk |
|---|---|
| 1 | len(rader), len(rader[0]) |
| 2 | rader[1][-1], rader[-1][0] |
| 3 | rader[0][1:] |
| 4 | rader[0] + rader[2] |
Alternativer: (i)
3 3, (ii) 5 6, (iii) [2, 3], (iv) [1, 2, 3, 6], (v) [3].Hva skriver programmet ut? Skriv utskriften nøyaktig, linje for linje.
def snitt(verdier, start=0):
total = start
for v in verdier:
total += v
return total / len(verdier)
tall = [2, 4, 9]
print(snitt(tall))
print(f'{snitt(tall, 5):.2f}')
print(snitt(tall) == 5.0)Hva skriver programmet ut? Skriv utskriften nøyaktig, linje for linje.
stasjoner = {'nord': [3, 8], 'sor': [2]}
antall = 0
for navn in stasjoner:
for v in stasjoner[navn]:
antall += 1
print(navn[0], v, end=' ')
print(antall, end=' | ')
print()
print(len(stasjoner), antall)- #1 — av-én i range. Stoppverdien er aldri med. n intervaller krever range(n + 1).
- #12 — grenser i nøstede løkker. Er den indre grensen et uttrykk i den ytre løkkevariabelen, må du telle på nytt for hver ytre runde.
- #18 — indeksering mot utsnitt. Element eller liste? Det avgjør hvordan svaret skrives.
- #5 — + på lister skjøter. Elementvis addisjon finnes ikke før NumPy.
- #16 — utskriftsdetaljer. end, sep, mellomrommet mellom print-argumenter, og bredde og desimaler i f-strenger.
- #17 — zip stopper ved korteste, og enumerate gir indeksen først.
- #13 — scope. Tilordning inne i en funksjon er lokal; append og innsetting i en ordbok er det ikke.
- #15 — heltall og presedens. // runder nedover, ** regnes fra høyre, / gir alltid flyttall.
Merk at rekursjon — at en funksjon kaller seg selv — er utenfor pensum i dette emnet og aldri forutsettes i en sporingsoppgave. Alt du møter, er løkker.
Begrepsbank
Begrepsbanken er flashcard-/repetisjonsstoff — den gjentar det du nettopp har lest. Hopp trygt over ved førstegangslesing; tidsanslaget for kapitlet gjelder kjernestoffet.
Tabellen du fører mens du kjører koden i hodet: én kolonne per variabel som endrer seg, én rad per iterasjon, og en siste kolonne med «utskrift så langt».
Tavlen er ikke pynt. På en fritekstoppgave gir en synlig, delvis riktig tavle uttelling selv om sluttsvaret bommer, fordi den viser at du har riktig forståelse av løkkestrukturen.
Antall runder avgjøres før du begynner å regne. range(a, b) gir b - a runder; range(a, b, s) gir antallet steg som får plass før b; en for over en liste gir len(liste) runder; en zip gir like mange som den korteste.
For nøstede løkker: tell den indre for hver ytre runde, og legg sammen. Er den indre grensen range(i), blir totalen — en trekant, ikke et rektangel.
Spørsmålet du skal stille ved hvert uttrykk: gir dette ett element eller en samling?
Indeksering, sum, len og et oppslag i en ordbok gir ett element. Utsnitt, liste-skjøting, .split() og .keys() gir en samling. Svaret avgjør hvordan utskriften ser ut — med eller uten hakeparenteser — og det er der halvparten av poengene i sjanger A ligger.
På fritekstoppgaver rettes sporing linje for linje: hver riktig utskriftslinje teller, og riktig struktur belønnes selv med en feil i detaljene.
På flervalg gir blankt og feil det samme, og negativ scoring er fjernet fra 2024/2025. Praktisk konsekvens: kryss alltid av, og skriv alltid ned den sporingen du har rukket, selv om du ikke er ferdig.
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.