5.3 DRILL — «Forklar med én setning» og «fyll inn manglende kodelinje»
De to distinktive NTNU-sjangrene: beskrive en funksjons formål presist i én setning (C), og velge kodelinjen som får funksjonen til å virke (D).
- Sjanger C — «forklar med én setning hva funksjonen gjør». Du får en ferdig funksjon og skal si hva den oppnår, i én setning. Den står i ~60 % av settene, altså 6 av de 10 sittingene i arkivet 2014–2019, og er verdt ~2 %.
- Sjanger D — «fyll inn manglende kodelinje». Én linje i en ellers ferdig funksjon er byttet ut med en markør, og du skal velge hvilken av fire linjer som hører hjemme der. Den står i ~50 % av settene, og er verdt 1–3 %.
Konsishet vurderes eksplisitt i sjanger C. Sensor gjentar det hvert år: svar kort og klart — er svaret uklart eller lengre enn nødvendig, trekker det ned. Det er den eneste sjangeren i faget der et riktig svar kan gi trekk fordi det er for langt.
Prioritet: må kunne — det midterste av bokas tre nivåer, som er «må sitte», «må kunne» og «bør kjenne til».
Kapitlet tar ~75 minutter og er delt i to halvdeler med hver sin løsningsoppskrift: sjanger C først (~35 min), deretter sjanger D (~40 min). Halvdelene er uavhengige — ta dem gjerne på hver sin kveld.
Begge sjangrene består i å LESE kode, ikke skrive den. Du trenger derfor ingen nye konstruksjoner, men du må kjenne igjen de tre mønstrene under på et blunk. Her er de, kjørt og ferdig oppfrisket.
1. Telleren med ordbok (kap. 3.5) — «hvor mange av hver?»:
linjer = ['tog', 'buss', 'tog']
d = {}
for t in linjer:
d[t] = d.get(t, 0) + 1
print(d)Utskrift:
{'tog': 2, 'buss': 1}2. Finn beste (kap. 3.1) — løkka som holder på den beste verdien sett så langt:
priser = [42, 19, 87, 31]
beste = priser[0]
for p in priser:
if p > beste:
beste = p
print(beste)Utskrift:
873. Bygg ei ny liste (kap. 3.2) — den tomme lista som fylles i løkka:
tall = [4, 7, 2, 9]
resultat = []
for t in tall:
if t % 2 == 1:
resultat.append(t)
print(resultat)Utskrift:
[7, 9]Du trenger dessuten løkkegrensene fra kap. 1.3, utsnitt fra kap. 3.3, sporingstavla fra kap. 5.1 og rekursjonsmalen fra kap. 5.2.
Første halvdel — «forklar med én setning» (~35 min)
Sjangeren ser lett ut og er det ikke. Nesten alle klarer å forstå hva koden gjør; det som skiller besvarelsene, er om de klarer å si det kort.
Tenk på det slik: du skal skrive den ene setningen som ville stått over funksjonen som en kommentar. Ikke en bruksanvisning, ikke en gjennomgang — en overskrift.
Etter løsningsoppskrifta følger seks korte oppslag. De er flashcard-stoff — hopp trygt over ved førstegangslesing; det du trenger for å komme i gang, står i oppskrifta og i eksempel 1.
Fire steg. De tar under et minutt til sammen når du har trent litt.
1. Les hele funksjonen, og se på return først. Returverdien forteller deg nesten alltid hva funksjonen er til for. Returnerer den et tall, ei liste, en ordbok eller en sannhetsverdi?
2. Spør: hva OPPNÅS? Ikke hvordan. «Går gjennom lista og sammenligner hvert element» er hvordan. «Finner det største tallet» er hva.
3. Skriv én setning som starter med et verb. «Returnerer …», «Teller …», «Sorterer …», «Sjekker om …». Ingen innledning, ingen «denne funksjonen».
4. Stryk alt om enkeltlinjer, løkkevariabler og innrykk. Er setningen fortsatt sann og entydig? Da er du ferdig.
Tommelfingerregel for lengden: én setning, som regel 6–15 ord. Blir den lengre enn to linjer på arket, har du begynt å beskrive hvordan.
Oppgaveformen der du får en ferdig funksjon og skal beskrive formålet i én setning. Den kommer ofte som en deloppgave rett etter en sporingsoppgave på den samme koden: først «hva returnerer myst(48, 18)?», så «forklar med én setning hva myst gjør».
Poengsummen er lav — rundt 2 % — men den er så godt som gratis når du først har sporet funksjonen, fordi du allerede har lest den.
Skillet hele sjangeren står på.
| Hvordan (feil svar) | Hva (riktig svar) |
|---|---|
| «Løkka går gjennom lista og sammenligner hvert element med det største så langt» | «Returnerer det største tallet i lista» |
«Den tar n % 10 og legger til resultatet av kallet med n // 10» | «Returnerer tverrsummen av et positivt heltall» |
«For hver rad legger den inn en verdi i en ordbok med get» | «Teller hvor mange ganger hver verdi forekommer» |
Et hvordan-svar er ikke galt — det er bare svar på et annet spørsmål, og det trekker ned fordi det er lengre enn nødvendig.
Begynn setningen med verbet, i presens, uten subjekt: «Returnerer …», «Teller …», «Sjekker om …», «Sorterer …», «Fjerner …», «Bygger …».
Grunnen er ikke stil, men presisjon: verbet tvinger deg til å si hva funksjonen gjør med en gang, og du slipper innledninger som «Denne funksjonen tar inn en liste og …», som koster plass uten å si noe.
De seks verbene over dekker nesten alle funksjoner du møter i faget.
Se på return-linja først. Typen på det som returneres, avgrenser svaret ditt før du har lest resten:
| Returnerer | Da er funksjonen som regel |
|---|---|
| ett tall | en teller, en sum eller et «finn beste» |
True/False | en sjekk — «Sjekker om …» |
| ei liste | et filter, en omforming eller et uttrekk |
| ei liste av lister | en tabelloperasjon, ofte transponering |
| en ordbok | en teller eller en gruppering |
-1 i tillegg til et tall | et søk, der -1 betyr «ikke funnet» |
Er det flere
return-linjer, ser du på dem alle: en funksjon som returnerer både True og False er alltid en sjekk.Sensors formulering, som gjentas hvert år: svar kort og klart. Er svaret uklart eller lengre enn nødvendig, trekker dette ned.
Regelen gjelder alle fritekstsvar i faget, men den er skarpest her, fordi oppgaven uttrykkelig ber om én setning. To riktige setninger er dårligere enn én riktig setning.
Praktisk konsekvens: skriv svaret, les det, og stryk alt som ikke trengs for at det skal være sant og entydig. Det du stryker, er poengene du ellers ville tapt.
Bokas feilkatalog nummererer den vanligste feilen i sjangeren: å beskrive hvordan koden virker linje for linje i stedet for hva funksjonen oppnår.
Feilen har tre kjennetegn, og har du ett av dem, har du som regel alle tre: svaret nevner løkkevariabler ved navn, det er lengre enn to setninger, og det kunne vært skrevet uten å skjønne hva funksjonen er til for.
Motgiften er steg 4 i løsningsoppskriften: stryk alt om enkeltlinjer, og se om setningen fortsatt er sann.
Her er den samme funksjonen forklart tre ganger. Bare den ene gir full uttelling.
Funksjonen: def f(x): return sorted(set(x))
For langt (#13 — trekker ned): «Funksjonen tar inn en liste som heter x. Først lager den et sett av lista, noe som fjerner alle duplikatene fordi et sett bare kan inneholde hver verdi én gang. Deretter sorterer den settet med sorted, som gir ei ny liste i stigende rekkefølge, og denne lista returneres.» — Alt er sant. Det er en beskrivelse av hvordan, den er seks ganger for lang, og den trekker ned.
For upresist (trekker ned): «Rydder opp i lista.» — Kort nok, men sier ikke hva som skjer. Sensor kan ikke se om du har forstått koden.
Riktig: «Returnerer de unike verdiene i lista, sortert stigende.» — Ett verb, ett objekt, én presisering. Ferdig.
Legg merke til at det korte svaret nevner begge effektene: duplikatene forsvinner, OG rekkefølgen blir sortert. Nevner du bare den ene, er svaret ufullstendig.
Forklar med én setning hva funksjonen gjør.
def beh(tabell):
ny = []
for j in range(len(tabell[0])):
rad = []
for i in range(len(tabell)):
rad.append(tabell[i][j])
ny.append(rad)
return nyreturn. Den returnerer ny, som er ei liste der hvert element selv er ei liste. Altså en tabell. Det utelukker med én gang at funksjonen er en teller eller en sjekk.Margnotat: dette steget alene halverer mulighetene, og det tar fem sekunder.
Steg 2 — hva oppnås? Den ytre løkka går over kolonnene, den indre over radene, og tabell[i][j] plukkes ut i den rekkefølgen. Rader blir kolonner. La oss bekrefte med et minimalt eksempel:
def beh(tabell):
ny = []
for j in range(len(tabell[0])):
rad = []
for i in range(len(tabell)):
rad.append(tabell[i][j])
ny.append(rad)
return ny
print(beh([[1, 2], [3, 4], [5, 6]]))Utskrift:
[[1, 3, 5], [2, 4, 6]]Tre rader og to kolonner inn; to rader og tre kolonner ut. Det er transponering.
Steg 3 — én setning med verbstart:
Returnerer den transponerte tabellen, altså den samme tabellen med rader og kolonner byttet om.
Steg 4 — stryk resten. Ingenting om i, j, append eller løkkene. De er hvordan.
Kontrast — slik ser et svar som trekker ned ut:
«Funksjonen lager først ei tom listeny. Så går den gjennom alle kolonnene medj, og for hver av dem lager den ei ny tom listerad. Inne i den løkka går den gjennom alle radene mediog leggertabell[i][j]inn irad. Til slutt leggesradinn iny, ognyreturneres.»
Alt er riktig, og likevel gir det trekk: det er en oversettelse av koden til norsk, ikke en forklaring. Sensor leter etter ordet «transponerer» eller «bytter om rader og kolonner» — og finner det ikke.
Margnotat: har du sporet funksjonen først, som i kap. 5.1, har du allerede sett formen på svaret. Det er derfor de to sjangrene henger sammen på settet.
(Innstegsoppgave, sjanger C — «forklar med én setning hva funksjonen gjør».) Skriv én setning som starter med et verb.
def f(x):
total = 0
for v in x:
if v < 0:
total = total + 1
return totalForklar med én setning hva funksjonen gjør.
def g(tekst):
d = {}
for bokstav in tekst:
d[bokstav] = d.get(bokstav, 0) + 1
beste = ''
for k in d:
if beste == '' or d[k] > d[beste]:
beste = k
return besteForklar med én setning hva funksjonen gjør.
def h(x):
resultat = []
for v in x:
if v not in resultat:
resultat.append(v)
return resultatFunksjonen er rekursiv. Forklar med én setning hva den gjør.
def m(a, b):
if b == 0:
return a
return m(b, a % b)Forklar med én setning hva funksjonen gjør. Vær nøye med kanttilfellet.
def s(tabell, kolonne):
k = tabell[0].index(kolonne)
beste = None
for rad in tabell[1:]:
if beste is None or float(rad[k]) > float(beste[k]):
beste = rad
return besteForklar med én setning hva funksjonen gjør.
def p(s):
ren = ''
for tegn in s:
if tegn != ' ':
ren = ren + tegn
return ren == ren[::-1]Forklar med én setning hva funksjonen gjør. Vær nøye med hva som returneres når verdien ikke finnes.
def q(x, maal):
for i in range(len(x)):
if x[i] == maal:
return i
return -1Andre halvdel — «fyll inn manglende kodelinje» (~40 min)
— naturlig pausepunkt —
I sjanger D får du en nesten ferdig funksjon der én linje er byttet ut med markøren #KODE1, og fire alternativer å velge mellom. Dette er et flervalg, og det er en av de få oppgavene i faget der du kan kontrollere svaret ditt helt mekanisk før du krysser av.
Det er ingen minuspoeng på flervalg fra 2015. Blankt teller som feil, så kryss alltid av — også når du bare har eliminert to alternativer.
Også her følger seks korte oppslag etter løsningsoppskrifta. De er flashcard-stoff — hopp trygt over ved førstegangslesing; eksempel 2 viser metoden i arbeid.
Fire steg. Steg 3 er det som skiller full pott fra gjetting.
1. Les spesifikasjonen først. Hva SKAL funksjonen gjøre? Står det ikke i oppgaveteksten, står det i funksjonsnavnet og i return-linja. Uten spesifikasjonen kan fire linjer se like riktige ut.
2. Les koden rundt hullet nøyaktig. Hvilke variabler finnes på det punktet? Hva er løkkegrensene? Hva slags verdi ligger allerede i variabelen linja skal oppdatere?
3. Prøv HVERT alternativ mot et minimalt eksempel. To eller tre elementer holder. Regn ut hva funksjonen ville returnert med hver av de fire linjene. Dette er ikke sløsing med tid — det er hele metoden.
4. Velg linja som gir riktig oppførsel også i kanttilfellet. Ofte gir to alternativer riktig svar for det enkle eksemplet, og bare det ene gir riktig svar for tom liste, for første runde, eller for siste element.
Tidsbruk: to–tre minutter. Er du usikker etter det, kryss av på det beste alternativet og gå videre — det koster ingenting å ta feil.
Oppgaveformen der én linje i en ellers komplett funksjon er erstattet med markøren #KODE1, og du velger mellom fire kandidatlinjer.
Linja som mangler, er nesten alltid en av fire typer: en løkkegrense, en betingelse, en oppdatering av en variabel, eller en indeksberegning. Kjenner du de fire typene, vet du hva du skal se etter.
Markøren er en vanlig Python-kommentar, og den står nøyaktig der linja skulle vært — med det innrykket linja skal ha.
Innrykket er informasjon: står markøren inne i løkka, skal linja kjøres hver runde; står den etter løkka, skal den kjøres én gang. Det alene eliminerer av og til to alternativer.
Merk at kode med #KODE1 i seg ikke er kjørbar som den står. I denne boka er slike blokker derfor merket som ikke-kjørbare.
Metoden som gjør sjanger D mekanisk: velg det minste mulige inndataeksempelet — to eller tre elementer — og regn ut hva funksjonen returnerer med hvert av de fire alternativene.
Et godt minimaleksempel har tre egenskaper: det er lite nok til å regnes i hodet, det gir forskjellig svar for de forskjellige alternativene, og det inneholder det som skiller alternativene — for eksempel både et partall og et oddetall, eller både første og siste element.
Vær alltid ferdig med ett alternativ før du begynner på det neste. Blander du dem, ender du med å gjette.
Den vanligste D-oppgaven handler om en løkkegrense, og de fire alternativene skiller seg med nøyaktig én: range(len(x)) mot range(len(x) - 1) mot range(1, len(x)) mot range(0, len(x), 2).
Spørsmålene som avgjør:
- Skal det FØRSTE elementet være med? Da kan ikke rekka starte på 1.
- Sammenlignes elementet med naboen sin, x[i + 1]? Da må rekka stoppe én tidligere, ellers går indeksen ut over lista.
- Skal annethvert element behandles? Da må steget være 2 — og startverdien avgjør om det er de like eller de ulike indeksene.
Den nest vanligste D-oppgaven er linja som oppdaterer en variabel inne i en løkke: en akkumulator (total = total + v), en teller (antall = antall + 1), en beste-verdi (beste = v) eller en grense i et søk (imin = midt + 1).
Testen er alltid den samme: hva står i variabelen FØR linja, og hva må stå der ETTER? Alternativene skiller seg gjerne på om de bruker elementet v eller indeksen i, og på om de legger til eller tilordner på nytt.
I en godt laget D-oppgave er ett av de gale alternativene nesten riktig: det gir riktig svar for det enkleste eksempelet, og feil svar for ett bestemt tilfelle.
De tre vanligste formene:
- Linja gir riktig svar for ett element, men feil for to.
- Linja gir riktig svar når treffet ligger først, men feil når det ligger sist.
- Linja gir riktig svar for en ikke-tom liste, men avbryter programmet for den tomme.
Derfor holder det aldri med ett testeksempel. Finner du to alternativer som begge ser riktige ut, er det et signal om at du har funnet den nære distraktoren — og at du trenger et eksempel til som skiller dem.
Funksjonen skal returnere True hvis tall er et primtall, altså et heltall større enn 1 som ikke er delelig med noe annet enn 1 og seg selv. Én linje mangler. Hvilket alternativ hører hjemme i #KODE1?
def er_primtall(tall):
if tall < 2:
return False
for i in range(2, tall):
#KODE1
return False
return Truea) if tall % i != 0:
b) if tall % i == 0:
c) if i % tall == 0:
d) if tall // i == 0:
Blokken er merket som ikke-kjørbar fordi den mangler en linje.
False så snart den finner en divisor, og True hvis den kommer gjennom hele løkka uten å finne noen.Steg 2 — koden rundt hullet. Linja etter markøren er return False, med ett ekstra innrykk. Det betyr at #KODE1 er en if-linje, og at betingelsen må være sann når i ER en divisor.
Steg 3 — prøv hvert alternativ mot minimaleksemplet tall = 9. Løkka går over i lik 2, 3, 4, 5, 6, 7 og 8. Riktig svar er False, siden 3 deler 9.
| Alternativ | Hva skjer for tall = 9 | Resultat |
|---|---|---|
a) tall % i != 0 | sann allerede for i lik 2, siden 9 delt på 2 gir rest | False — men av feil grunn |
b) tall % i == 0 | usann for 2, sann for 3 | False — riktig |
c) i % tall == 0 | 2 % 9 er 2, aldri 0 for i mindre enn tall | True — feil |
d) tall // i == 0 | 9 // 2 er 4, aldri 0 her | True — feil |
Steg 4 — kanttilfellet skiller a) fra b). Begge gir
False for 9, men prøv tall = 7, som ER et primtall og skal gi True:- Med a) er
7 % 2 != 0 sann med én gang, og funksjonen returnerer False. Feil.- Med b) er ingen av testene sanne, løkka går til ende, og funksjonen returnerer
True. Riktig.Riktig svar: b)
Den ferdige funksjonen, kjørt:
def er_primtall(tall):
if tall < 2:
return False
for i in range(2, tall):
if tall % i == 0:
return False
return True
print(er_primtall(7), er_primtall(9), er_primtall(2), er_primtall(1))Utskrift:
True False True FalseMargnotat: legg merke til at alternativ a) var «riktig nok» for det første eksemplet. Det er nettopp derfor steg 4 finnes — et eneste testeksempel er ikke nok når to alternativer er nære hverandre.
(Innstegsoppgave, sjanger D — «fyll inn manglende kodelinje».) Funksjonen skal summere alle tallene i lista. Hvilket alternativ hører hjemme i #KODE1?
def summer(x):
total = 0
for v in x:
#KODE1
return totala) total = v
b) total = total + v
c) total = total + 1
d) total = v + 1
Blokken er merket som ikke-kjørbar fordi den mangler en linje.
Funksjonen skal kvadrere annethvert tall i lista, med start på det ANDRE tallet, og endre lista der den står. Hvilket alternativ hører hjemme i #KODE1?
def kvadrer_annethvert(x):
#KODE1
x[i] = x[i] ** 2
return xa) for i in range(0, len(x), 2):
b) for i in range(1, len(x)):
c) for i in range(1, len(x), 2):
d) for i in range(2, len(x), 2):
Blokken er merket som ikke-kjørbar fordi den mangler en linje.
Funksjonen skal returnere det største tallet i lista. Hvilket alternativ hører hjemme i #KODE1?
def storst(x):
beste = x[0]
for v in x:
if v > beste:
#KODE1
return bestea) beste = v
b) beste = beste + v
c) v = beste
d) return v
Blokken er merket som ikke-kjørbar fordi den mangler en linje.
Funksjonen skal telle hvor mange ganger hver verdi forekommer, og returnere en ordbok. Hvilket alternativ hører hjemme i #KODE1?
def tell(x):
d = {}
for v in x:
#KODE1
return da) d[v] = d[v] + 1
b) d[v] = 1
c) d[v] = d.get(v, 0) + 1
d) d.get(v, 0) + 1
Blokken er merket som ikke-kjørbar fordi den mangler en linje.
Funksjonen skal sjekke om lista er sortert stigende ved å sammenligne hvert element med det neste. Hvilket alternativ hører hjemme i #KODE1?
def er_sortert(x):
#KODE1
if x[i] > x[i + 1]:
return False
return Truea) for i in range(len(x)):
b) for i in range(1, len(x)):
c) for i in range(len(x) + 1):
d) for i in range(len(x) - 1):
Blokken er merket som ikke-kjørbar fordi den mangler en linje.
Funksjonen leser en tekstfil der hver linje har tre felt skilt med semikolon, og skal returnere en 2D-tabell der hver rad er ei liste av de tre feltene, uten linjeskift. Hvilket alternativ hører hjemme i #KODE1?
def les_tabell(filnavn):
tabell = []
f = open(filnavn, 'r')
for linje in f:
#KODE1
f.close()
return tabella) tabell.append(linje.split(';').strip())
b) tabell.append(linje.strip().split(';'))
c) tabell.append(linje.split(';'))
d) tabell.append(linje.strip())
Blokken er merket som ikke-kjørbar fordi den mangler en linje.
Funksjonen leter etter en verdi i ei sortert liste ved å halvere søkeområdet for hvert steg. Den returnerer indeksen til treffet, eller −1. Hvilket alternativ hører hjemme i #KODE1?
def finn(x, maal):
imin = 0
imax = len(x) - 1
while imin <= imax:
#KODE1
if x[imid] == maal:
return imid
elif x[imid] < maal:
imin = imid + 1
else:
imax = imid - 1
return -1a) imid = (imin + imax) / 2
b) imid = (imin - imax) // 2
c) imid = (imin + imax) // 2
d) imid = len(x) // 2
Blokken er merket som ikke-kjørbar fordi den mangler en linje. Algoritmen bak — halveringssøket — hører til algoritmedelen senere i boka; her trenger du bare å lese koden som står der.
Etter de sju D-oppgavene over kjenner du igjen hulltypen på innrykket og på koden rundt. Bruk denne tabellen som sjekkliste:
| Ser du dette rundt hullet | Da mangler |
|---|---|
| Markøren står alene på en linje som avsluttes med kolon i alternativene | en løkke- eller if-linje |
| Linja under har ekstra innrykk | en if- eller for-linje |
Kroppen bruker x[i + 1] | en løkkegrense som stopper én tidligere |
| En variabel er satt før løkka og returneres etter den | oppdateringslinja for den variabelen |
| En tom ordbok er opprettet over løkka | telleren eller grupperingslinja |
| To grenser oppdateres i grenene under | en midtpunktberegning med // |
| Fila åpnes rett over | parsingen linje.strip().split(sep) |
Og husk: kryss alltid av. Det er ingen minuspoeng på flervalg fra og med 2015, så et gjettet svar er strengt bedre enn et blankt.
- #13 — ordrik forklaring. Å beskrive hvordan linje for linje i stedet for hva funksjonen oppnår. Den dyreste og vanligste.
- For upresist svar. «Rydder i lista» sier ingenting sensor kan vurdere.
- Mellomsteget forvekslet med formålet. Bygger funksjonen en ordbok for å finne én verdi, er det verdien som er poenget.
- Kanttilfellet oversett. Returnerer funksjonen None eller −1 i et tilfelle, hører det med i setningen.
I sjanger D:
- Å velge uten å teste. Det alternativet som «ser riktig ut», er ofte den nære distraktoren.
- Å teste med bare ett eksempel. To alternativer gir gjerne samme svar for det enkle tilfellet.
- #5 — av-med-én i grensealternativene, særlig når kroppen bruker x[i + 1].
- #9 — / der // skal stå. En indeks må være et heltall.
- Å overse manglende tilordning. d.get(v, 0) + 1 uten likhetstegn regner riktig og lagrer ingenting.
- Å la et flervalg stå blankt. Blankt teller som feil.
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 Norges teknisk-naturvitenskapelige universitet. Dette er ikke offisielt studiemateriell. Les mer.