7.3 DRILL — Ledd 2: parsing og datatransformasjon
Gjøre om enkeltstrenger og rader til tall og strukturer — list_from_string, str_to_numbers, tidsstrenger og make_dict — leddet mellom rådata og analyse.
- Parsing står i 100 % av settene (10 av 10 sittinger i arkivet 2014–2019) og er verdt 5–10 %. Det er sjanger F, altså «gjør om dataene til noe du kan regne på».
- De faste jobbene er de samme hvert år: gjør en liste av felt om til tall, gjør en tidsstreng som '1:04.32' om til sekunder, håndtér et varierende antall mellomrom, og bygg en ordbok av en tabell.
- Feilene her er stumme. Glemmer du konverteringen, krasjer ingenting — programmet sammenligner tekst i stedet for tall og gir feil svar med full selvtillit.
- Tidsregningen er den ene delen som krever litt hoderegning: // gir heltallsdelen, % gir resten, og de to brukes alltid sammen.
Prioritet: må sitte — det høyeste av bokas tre nivåer.
Kapitlet tar ~75 minutter og er delt i seks løkker med tidsanslag. Naturlig pausepunkt etter løkke 3.
Ledd 2 bygger på tre ting du allerede har. Her er de, ferdig oppfrisket.
1. Del en linje og konverter et felt (kap. 3.3):
felt = '14.2;85'.split(';')
print(float(felt[0]) + int(felt[1]))Utskrift:
99.22. Heltallsdeling og rest (kap. 1.1) — de to som deler et tall i to biter:
print(197 // 60, 197 % 60)Utskrift:
3 173. Ordboka bygget fra en rad (kap. 3.5):
d = {}
rad = ['R1', 'Elvestien', 14.2]
d[rad[0]] = rad[1:]
print(d)Utskrift:
{'R1': ['Elvestien', 14.2]}I tillegg forutsetter kapitlet kap. 7.2, der fila ble lest inn, og kap. 3.4, der formatert utskrift ble gjennomgått.
Løkke 1 — fra felt til tall (~12 min)
Ledd 1 gir deg lister av strenger. Ledd 2 gjør dem om til noe du kan regne med. Det er hele jobben, og den har to standardformer.
Ledd 1 leverte deg rader som ['R1', 'Elvestien', '14.2', '85']. Alle fire elementene er tekst, også de to som ser ut som tall.
Parsing i ledd 2 er å gjøre hvert felt om til den verdien det egentlig representerer: '14.2' blir flyttallet 14.2, '85' blir heltallet 85, '1:04.32' blir 64.32 sekunder.
Testen på om du er ferdig med ledd 2: kan analysefunksjonen i ledd 3 sammenligne, summere og sortere dataene uten å konvertere noe selv? Da er du ferdig.
Den vanligste bestillingen i sjanger F: en funksjon som tar én streng og gir tilbake ei liste med tall.
tekst_til_liste('4 9 2 7') -> [4, 9, 2, 7]Blokken over viser hva funksjonen skal gjøre, ikke hvordan.
Oppgaveteksten kaller den ofte list_from_string eller str_to_numbers — engelske navn er like gyldige som norske, og fasitene i dette faget blander. Bruk navnet oppgaven oppgir.
To steg inni: .split(...) for å dele, og int() eller float() på hvert felt.
Motstykket: funksjonen tar imot ei ferdig delt liste og gir tilbake den samme lista med tall i.
Den eksplisitte formen er ei ny liste bygget med append:
ut = []
for x in felt:
ut.append(float(x))Blokken over er et utdrag, ikke et program.
Å endre lista på plass (felt[i] = float(felt[i]) i en for i in range(len(felt)):-løkke) er også riktig — men da endrer du samtidig lista til den som kalte deg, og det kan overraske. Bygg heller ei ny liste når oppgaven ikke sier noe annet.
En listekomprehensjon bygger ei ny liste på én linje: «for hvert x i felt, gi meg float(x)».
tall = [float(x) for x in felt]Den gjør nøyaktig det samme som løkka med append, og de er sidestilt — begge gir full pott. Fasitene i dette faget bruker snarveier fritt.
Rådet under tidspress er likevel å skrive løkka hvis du er det minste usikker: en listekomprehensjon med en feil i er vanskeligere å rette på papir enn tre linjer med løkke.
tekst_til_liste(s) som tar en streng med tall skilt av mellomrom og returnerer ei liste med flyttall. Skriv deretter snitt(liste) som returnerer gjennomsnittet.»def tekst_til_liste(s):
ut = []
for felt in s.split():
ut.append(float(felt))
return ut
print(tekst_til_liste('64.32 58.91 72.07'))Utskrift:
[64.32, 58.91, 72.07]Snittet, som gjenbruker den:
def tekst_til_liste(s):
return [float(felt) for felt in s.split()]
def snitt(liste):
return sum(liste) / len(liste)
print(snitt(tekst_til_liste('64.32 58.91 72.07')))Utskrift:
65.1Legg merke til at svaret har mange desimaler. Det er ikke en feil: 65.10000000000001 er nøyaktig det Python regner ut, fordi flyttall lagres binært og noen desimaltall ikke går opp. Trenger du et pent tall, formaterer du i utskriften:
print('{:.2f}'.format(65.10000000000001))Utskrift:
65.10Slik ville sensor sett på det: de to formene av tekst_til_liste er sidestilt. Det som gir uttelling, er at funksjonen returnerer lista og at snitt kaller den i stedet for å dele strengen på nytt.
Kantfeilen å nevne: snitt krasjer med ZeroDivisionError på ei tom liste. Sier ikke oppgaven noe om det, skriver du én setning: «antar at lista har minst ett element». Skal den være robust, er formen if len(liste) == 0: return None — se kap. 7.6.
(Innstegsoppgave, sjanger B — kodesporing, altså «hva skriver dette programmet ut?».) Hva skriver programmet ut? Skriv begge linjene nøyaktig.
felt = '9.8;430'.split(';')
print(felt[0] + felt[1])
print(float(felt[0]) + int(felt[1]))til_tall(felt) som tar ei liste av strenger og returnerer ei ny liste der hvert element er gjort om til heltall. Lista som kommer inn, skal ikke endres.»Skriv funksjonen med en løkke, og vis deretter snarveien med listekomprehensjon.
Løkke 2 — tidsstrengen inn (~14 min)
Tider skrives som tekst i datafiler: '1:04.32' betyr ett minutt, fire sekunder og 32 hundredeler. Du kan ikke sammenligne slike strenger direkte — '0:58.91' og '1:04.32' sammenlignes bokstav for bokstav, og '0' kommer før '1'. Denne gangen gir det tilfeldigvis riktig svar, men bare fordi minuttallet er ett siffer.
Løsningen er alltid den samme: gjør tiden om til ett tall først.
Formatet du møter oftest i dette faget: minutter, kolon, sekunder, punktum, hundredeler.
'1:04.32' er ett minutt, fire sekunder og 32 hundredeler — til sammen 64.32 sekunder.
Tre ting å legge merke til før du koder:
- Sekunddelen er nullpadd: 4 sekunder skrives 04, ikke 4.
- Det er ett kolon, så .split(':') gir nøyaktig to biter.
- Hundredelene henger sammen med sekundene i den andre biten, og float('04.32') takler dem uten videre.
Tre linjer, og de er de samme hver gang:
deler = tekst.split(':')
minutter = int(deler[0])
sekunder = float(deler[1])
return minutter * 60 + sekunderBlokken over er mønsteret; den ferdige funksjonen står i eksempel 2.
Har formatet tre ledd ('1:02:05' for time, minutt, sekund), utvides det på samme måte:
timer * 3600 + minutter * 60 + sekunderRegelen bak: gjør om til den minste enheten, altså sekunder. Da er alle sammenligninger, summeringer og sorteringer rett fram.
a // b gir heltallsdelen av divisjonen, a % b gir resten. De brukes alltid sammen når et tall skal deles i to enheter.print(197 // 60)
print(197 % 60)Utskrift:
3
17197 sekunder er altså 3 minutter og 17 sekunder.
To feller: / gir alltid flyttall, så 197 / 60 er 3.2833… og ikke 3 — det er feilkode #9, blandet heltalls- og flyttallsdeling. Og // runder nedover, også for negative tall: -7 // 2 er -4, ikke -3.
stevne.txt har én linje per svømmer: navn, klubb og tid, skilt med semikolon. Tiden er på formen minutter:sekunder.hundredeler. Skriv tid_til_sekunder(tekst) som gir tiden som flyttall, og les_stevne(filnavn) som returnerer en 2D-liste der tiden allerede er gjort om.»Slik ser fila ut:
Innholdet i stevne.txt:
Ida Nervik;Havorn;1:04.32
Jonas Lie;Delfin;0:58.91
Amina Kaur;Havorn;1:12.07
Petter Ruud;Storm;1:04.09Omregningen:
def tid_til_sekunder(tekst):
deler = tekst.split(':')
minutter = int(deler[0])
sekunder = float(deler[1])
return minutter * 60 + sekunder
print(tid_til_sekunder('1:04.32'))
print(tid_til_sekunder('0:58.91'))Utskrift:
64.32
58.91Innlesingen som gjenbruker den:
def tid_til_sekunder(tekst):
deler = tekst.split(':')
return int(deler[0]) * 60 + float(deler[1])
def les_stevne(filnavn):
f = open(filnavn, 'r')
tabell = []
for linje in f:
felt = linje.strip().split(';')
felt[2] = tid_til_sekunder(felt[2])
tabell.append(felt)
f.close()
return tabell
for rad in les_stevne('stevne.txt'):
print(rad)Utskrift:
['Ida Nervik', 'Havorn', 64.32]
['Jonas Lie', 'Delfin', 58.91]
['Amina Kaur', 'Havorn', 72.07]
['Petter Ruud', 'Storm', 64.09]Slik ville sensor sett på det. Deloppgaven med tid_til_sekunder er typisk 5–10 %, og de tre tingene som gir uttelling er: delingen på kolon, * 60 på minuttdelen, og at funksjonen returnerer et tall i stedet for å skrive det ut.
At les_stevne kaller tid_til_sekunder i stedet for å gjenta regnestykket, er det som gir gjenbrukspoengene i den neste deloppgaven.
Flere korrekte løsninger: minutter, sekunder = tekst.split(':') med utpakking rett i tilordningen er like riktig, og kortere. Begge gir full pott.
Kantfeilen: kommer en tid uten kolon ('58.91'), krasjer deler[1] med IndexError. Nevn det i én setning hvis fila kan være slik — her sa oppgaven at formatet er fast.
Fire feller, og de tre første er stumme:
- #1 — glemt int()/float(). Tekst sammenlignes bokstav for bokstav, så '9' er større enn '10'.
- #3 — feil skilletegn. .split() uten argument på en linje der feltene er skilt med semikolon gir ett felt tilbake.
- #9 — / der du mente //. 197 / 60 er 3.2833…, og str(3.2833) i en tidsutskrift ser umiddelbart rart ut — dette er den snilleste av de fire.
- Å sammenligne tidsstrenger direkte. '10:05' < '9:30' er sant, fordi '1' kommer før '9' i tekstsammenligning. Gjør alltid om til tall først.
raskeste(tabell) som tar 2D-lista fra les_stevne og returnerer navnet på svømmeren med lavest tid.»Tiden ligger i kolonne 2 og er allerede et flyttall.
Hva skriver programmet ut? Forklar den siste linja med én setning.
tider = ['10:05', '9:30', '11:00']
print(sorted(tider))
print(min(tider))Løkke 3 — tidsstrengen ut (~12 min)
Analysen din regner i sekunder. Brukeren vil se 00:30:42. Veien tilbake er // og %, brukt to ganger.
— naturlig pausepunkt etter denne løkka —
Tre uttrykk, alltid i denne rekkefølgen:
timer = sekunder // 3600
minutter = (sekunder % 3600) // 60
rest = sekunder % 60Blokken over er mønsteret; den ferdige funksjonen står i eksempel 3.
Logikken: // 3600 tar ut hele timer, % 3600 gir det som er igjen etter timene, og av det tar // 60 ut hele minutter. % 60 gir sekundene som blir til overs helt til slutt.
Skal du bare ha mm:ss, dropper du første linje og bruker sekunder // 60 og sekunder % 60. Mønsteret er gjennomgått i kap. 3.4; her drilles det i kjedesammenheng.
str(t).zfill(2) fyller på en null foran til strengen er minst to tegn lang. str(5).zfill(2) gir '05', str(42).zfill(2) gir '42'.Uten den blir klokkeformatet 0:30:42 skrevet som 0:30:42 i beste fall og 0:3:42 i verste — og eksamensoppgavene ber uttrykkelig om hh:mm:ss med to sifre.
De to andre formene som gir det samme, er '{:02d}'.format(t) og f-strengen f'{t:02d}'. Alle tre er sidestilt. f-strenger er nyere enn arkivet 2014–2019 (verifiser mot gjeldende pensum — varslet inn 2019), så boka viser dem sammen med .format.
Skal sekunddelen ha desimaler og nullpadding, gjør '{:05.2f}'.format(x) begge deler: 5 er den totale bredden inkludert punktum og desimaler, .2f er to desimaler.
'{:05.2f}'.format(4.32) gir '04.32' — fem tegn: 0, 4, ., 3, 2.
Det er lett å telle feil her. Skriver du {:02.2f}, ber du om minst to tegn totalt, og siden 4.32 allerede er fire tegn, skjer det ingenting.
joggeturer.txt har dato, varighet i hele sekunder og distanse i kilometer, skilt med semikolon. Skriv klokkeformat(sekunder) som gir varigheten på formen hh:mm:ss, og skriv ut hele loggen med varigheten i klokkeformat.»Slik ser fila ut:
Innholdet i joggeturer.txt:
2026-05-02;1842;7.1
2026-05-05;2711;10.4
2026-05-09;1503;5.8Omregningen:
def klokkeformat(sekunder):
timer = sekunder // 3600
minutter = (sekunder % 3600) // 60
rest = sekunder % 60
return str(timer).zfill(2) + ':' + str(minutter).zfill(2) + ':' + str(rest).zfill(2)
print(klokkeformat(1842))
print(klokkeformat(2711))
print(klokkeformat(45))Utskrift:
00:30:42
00:45:11
00:00:45Hele loggen, med innlesingen fra kap. 7.2 gjenbrukt:
def klokkeformat(sekunder):
timer = sekunder // 3600
minutter = (sekunder % 3600) // 60
return str(timer).zfill(2) + ':' + str(minutter).zfill(2) + ':' + str(sekunder % 60).zfill(2)
f = open('joggeturer.txt', 'r')
for linje in f:
felt = linje.strip().split(';')
print(felt[0], klokkeformat(int(felt[1])), felt[2] + ' km')
f.close()Utskrift:
2026-05-02 00:30:42 7.1 km
2026-05-05 00:45:11 10.4 km
2026-05-09 00:25:03 5.8 kmSlik ville sensor sett på det: de tre uttrykkene med // og % er selve svaret, og zfill er detaljen som skiller full pott fra nesten. Skriver du str(timer) + ':' + ... uten padding, får du 0:30:42 — riktig regnet, men ikke det formatet oppgaven ba om.
Kontrollregningen (gjør den for hånd, det tar femten sekunder): 1842 sekunder. . , og . . Altså 00:30:42. Stemmer med utskriften.
Kantfeilen å kjenne til: funksjonen forutsetter et heltall. Sender du inn 1842.5, gir // et flyttall, og du får 0.0:30.0:42.5. Konverter med int() først hvis varigheten kan ha desimaler.
sekunder_til_tid(total) som gjør et flyttall med sekunder om til formen minutter:sekunder.hundredeler, altså den motsatte veien av tid_til_sekunder i eksempel 2. Sekunddelen skal alltid ha to sifre foran punktumet og to desimaler.»Test funksjonen på 64.32 og 58.91.
Bruk joggeturer.txt med dato, sekunder og kilometer.
a) Skriv fart(sekunder, km) som returnerer farten i minutter per kilometer, som flyttall.
b) Skriv beste_fart(tabell) som returnerer datoen for turen med lavest tid per kilometer. Tabellen har rader på formen [dato, sekunder, km] med tallene ferdig konvertert.
c) Skriv ut svaret på formen minutter:sekunder per kilometer.
Løkke 4 — felt som ikke er rene (~12 min)
Ikke alle felt er klare til float(). Noen har komma som desimaltegn, noen har en enhet bakpå, og noen er skilt av et vilkårlig antall mellomrom. Alle tre er faste innslag på eksamen, og alle tre løses med ett grep hver.
.split() uten argument deler på blanktegn og slår sammen flere på rad. Det er det eneste riktige valget når kolonnene er justert i stedet for skilt.linje = '2026-02-01 418 kjeller'
print(linje.split())
print(len(linje.split(' ')))Utskrift:
['2026-02-01', '418', 'kjeller']
9Med ett mellomrom som argument blir hvert ekstra mellomrom et eget, tomt felt — ni felt der du ventet tre.
Begrensningen: formen kan ikke brukes når et felt selv inneholder mellomrom, som 'Ida Nervik'. Da må fila ha et ekte skilletegn.
Norske datafiler skriver ofte 24,90 der Python krever 24.90. float('24,90') gir en ValueError.
s.replace(gammel, ny) gir en ny streng der alle forekomstene er byttet ut:
print(float('24,90'.replace(',', '.')))Utskrift:
24.9Pass på rekkefølgen når fila også bruker komma som skilletegn — da må du dele først og erstatte etterpå, ellers renner feltene sammen. Er begge deler komma, sier oppgaveteksten alltid hvordan den skal leses.
Et felt som '24,90 kr' har en enhet hengende ved. To rene grep, begge riktige:
- Del på mellomrom og ta første bit: felt.split()[0] gir '24,90'.
- Kutt et fast antall tegn: felt[:-3] fjerner de tre siste tegnene.
Den første er tryggere, fordi den virker enten enheten er kr, km eller kWh. Den andre krasjer stille hvis enheten skifter lengde.
Har feltet et prosenttegn uten mellomrom ('85%'), er .replace('%', '') det korteste grepet.
Les vannmaaler.txt, der kolonnene er justert med et varierende antall mellomrom, og kassalapp.txt, der prisen har både komma og enhet.
Innholdet i vannmaaler.txt:
2026-02-01 418 kjeller
2026-03-01 503 kjeller
2026-04-01 477 kjellerdef les_maaler(filnavn):
f = open(filnavn, 'r')
tabell = []
for linje in f:
felt = linje.split()
felt[1] = int(felt[1])
tabell.append(felt)
f.close()
return tabell
for rad in les_maaler('vannmaaler.txt'):
print(rad)Utskrift:
['2026-02-01', 418, 'kjeller']
['2026-03-01', 503, 'kjeller']
['2026-04-01', 477, 'kjeller']Merk at .strip() er droppet her: .split() uten argument fjerner linjeskiftet på kjøpet, fordi linjeskift også er et blanktegn. Å ha .strip() med er ikke feil — bare unødvendig.
Fila med komma og enhet:
Innholdet i kassalapp.txt:
melk;2;24,90 kr
kaffe;1;89,50 kr
egg;3;41,00 krdef les_lapp(filnavn):
f = open(filnavn, 'r')
tabell = []
for linje in f:
felt = linje.strip().split(';')
felt[1] = int(felt[1])
felt[2] = float(felt[2].split()[0].replace(',', '.'))
tabell.append(felt)
f.close()
return tabell
for rad in les_lapp('kassalapp.txt'):
print(rad)Utskrift:
['melk', 2, 24.9]
['kaffe', 1, 89.5]
['egg', 3, 41.0]Den ene tette linja gjør tre ting i rekkefølge: .split()[0] kaster enheten, .replace(',', '.') bytter desimaltegnet, float(...) gjør resten om til tall.
Slik ville sensor sett på det: kjeden av tre kall er lovlig og gir full pott, men den er vanskelig å lese. Deler du den i tre linjer med hvert sitt navn, får du den samme uttellingen og en lettere sjekk:
raa = '24,90 kr'
uten_enhet = raa.split()[0]
med_punktum = uten_enhet.replace(',', '.')
print(float(med_punktum))Utskrift:
24.9Kantfeilen: rekkefølgen er ikke likegyldig. float('24,90 kr'.replace(',', '.')) krasjer fortsatt, fordi ' kr' står igjen. Enheten må bort først.
rens_pris(tekst) som gjør en prisstreng som '129,50 kr' om til flyttallet 129.5. Funksjonen skal virke enten enheten står der eller ikke.»Test på '129,50 kr', '89,50' og '7,00 kr'.
Hva skriver programmet ut? Forklar forskjellen mellom de to linjene med én setning.
linje = '2026-03-01 503 kjeller'
print(len(linje.split()))
print(len(linje.split(' ')))Løkke 5 — fra tabell til ordbok (~13 min)
Den siste faste jobben i ledd 2: du har en ferdig 2D-liste, og en senere deloppgave vil slå opp på en nøkkel. Da bygger du ordboka av tabellen — uten å lese fila på nytt.
Fire linjer, og nøkkelkolonnen er det eneste du velger:
d = {}
for rad in tabell:
d[rad[k]] = rad
return dBlokken over er mønsteret; den ferdige funksjonen står i eksempel 5.
Tre varianter av verdien, alle riktige, valgt ut fra hva oppgaven skal svare på:
- d[rad[k]] = rad — hele raden, nøkkelen inkludert. Enklest å skrive, ingen forskyvning å huske.
- d[rad[k]] = rad[1:] — resten av raden. Kortere verdi, men indeksene forskyves.
- d[rad[k]] = rad[j] — bare én kolonne, når oppgaven bare spør om én verdi.
Har tabellen en overskriftsrad, må løkka gå over tabell[1:], og nøkkelkolonnen kan finnes med tabell[0].index(navn).
Oppgaveteksten kaller den ofte make_dict. Poenget er at nøkkelkolonnen er en parameter, ikke skrevet inn i koden:
lag_ordbok(tabell, 0) -> {'R1': [...], 'R2': [...]}
lag_ordbok(tabell, 1) -> {'Elvestien': [...], 'Toppturen': [...]}Blokken over viser to kall, ikke kode som kan kjøres.
Da kan den samme funksjonen brukes to ganger i samme besvarelse med hver sin nøkkel — og det er nøyaktig den slags gjenbruk sensor belønner.
Bygger du {rad[k]: rad} og to rader har samme verdi i kolonne k, forsvinner den første i stillhet.
Sjekken tar én linje, og den er verdt å ta med når du er usikker:
tabell = [['R1', 'Elvestien'], ['R2', 'Toppturen'], ['R1', 'Elvestien nord']]
nokler = []
for rad in tabell:
nokler.append(rad[0])
print(len(nokler), len(set(nokler)))Utskrift:
3 2Er de to tallene forskjellige, er nøkkelen ikke unik — og da skal du enten gruppere ({nøkkel: [rader]}) eller velge en annen kolonne. set(...) gir en samling uten duplikater, og lengden av den er antall forskjellige verdier.
Datafiler har hull. En linje som 'R4;;12.0;30' gir ['R4', '', '12.0', '30'] — feltet er tomt, ikke borte.
int('') og float('') krasjer begge med ValueError. Testen står før konverteringen:
if felt[2] == '':
felt[2] = 0
else:
felt[2] = float(felt[2])Blokken over er et utdrag fra innlesingsløkka.
Hva du setter inn i stedet, er en antakelse du skal skrive ned: 0 hvis det betyr «ingen», None hvis det betyr «ukjent». De to er ikke det samme, og valget kan endre svaret på analysen.
sykkelruter.txt har overskriftsraden rutenr;navn;lengde;stigning. Skriv lag_ordbok(tabell, k) som bygger en ordbok med kolonne k som nøkkel og hele raden som verdi. Bruk den til å slå opp både på rutenummer og på rutenavn.»Slik ser fila ut:
Innholdet i sykkelruter.txt:
rutenr;navn;lengde;stigning
R1;Elvestien;14.2;85
R2;Toppturen;9.8;430
R3;Havnerunden;6.5;20Innlesing og ordbokbygging, med overskriftsraden hoppet over i ordboka:
def les_ruter(filnavn):
f = open(filnavn, 'r')
tabell = [f.readline().strip().split(';')]
for linje in f:
felt = linje.strip().split(';')
felt[2] = float(felt[2])
felt[3] = int(felt[3])
tabell.append(felt)
f.close()
return tabell
def lag_ordbok(tabell, k):
d = {}
for rad in tabell[1:]:
d[rad[k]] = rad
return d
tabell = les_ruter('sykkelruter.txt')
paa_nummer = lag_ordbok(tabell, 0)
paa_navn = lag_ordbok(tabell, tabell[0].index('navn'))
print(paa_nummer['R2'])
print(paa_navn['Havnerunden'])
print(len(paa_navn))Utskrift:
['R2', 'Toppturen', 9.8, 430]
['R3', 'Havnerunden', 6.5, 20]
3Slik ville sensor sett på det: at lag_ordbok tar k som parameter er det som gjør at den kan kalles to ganger. En besvarelse med lag_ordbok_paa_nummer og lag_ordbok_paa_navn — to nesten like funksjoner — gjør den samme jobben, men mister gjenbrukspoengene.
Legg merke til at det andre kallet finner kolonnen med tabell[0].index('navn') i stedet for tallet 1. Det er det samme fullscore-grepet som i kap. 7.2: koden overlever en omstokket kolonnerekkefølge.
Kantfeilen: paa_navn['Havnerunda'] — med skrivefeil — gir KeyError. Skal oppslaget tåle en ukjent nøkkel, bruker du paa_navn.get('Havnerunda', None), som gir None i stedet for å krasje.
stevne.txt med rader på formen [navn, klubb, tid], uten overskriftsrad. Skriv tider_per_klubb(tabell) som returnerer {klubb: [tid, tid, …]} med alle tidene til hver klubb.»En fil har rader på formen rutenr;navn;lengde;stigning, men noen rader mangler stigningen — feltet er tomt.
a) Skriv les_robust(filnavn) som leser fila til en 2D-liste og setter stigningen til None når feltet er tomt.
b) Skriv snitt_stigning(tabell) som regner gjennomsnittlig stigning bare over radene der stigningen er kjent.
c) Skriv én setning om antakelsen du har gjort.
Løkke 6 — parseleddet i kjeden (~12 min)
Til slutt: hvor hører parsefunksjonene hjemme? Svaret avgjør om du får gjenbrukspoengene eller ikke.
tid_til_sekunder, rens_pris og til_tall skal stå som egne funksjoner og kalles fra innlesingen — ikke skrives rett inn i innlesingsløkka.Tre grunner, alle poenggivende:
- Oppgaven ber som regel om dem som egne deloppgaver med egne prosenter.
- De blir testbare: du kan kalle tid_til_sekunder('1:04.32') alene og se at svaret er 64.32.
- De blir gjenbrukbare: den samme funksjonen brukes i innlesingen, i utskriften og i menyen.
Regelen i én setning: én funksjon gjør én ting, og innlesingen kaller den.
Sett sammen innlesingen fra kap. 7.2 og parsefunksjonen fra eksempel 2, og legg til en utskrift som viser tidene tilbake i tidsformat. Hver funksjon i sin egen blokk, som deloppgavene på et sett.
def tid_til_sekunder(tekst):
deler = tekst.split(':')
return int(deler[0]) * 60 + float(deler[1])
def sekunder_til_tid(total):
minutter = int(total // 60)
return str(minutter) + ':' + '{:05.2f}'.format(total - minutter * 60)
print(tid_til_sekunder('1:12.07'))
print(sekunder_til_tid(72.07))Utskrift:
72.07
1:12.07Deloppgave b) — innlesingen som kaller a):
def les_stevne(filnavn):
f = open(filnavn, 'r')
tabell = []
for linje in f:
felt = linje.strip().split(';')
felt[2] = tid_til_sekunder(felt[2])
tabell.append(felt)
f.close()
return tabellDeloppgave c) — utskriften som kaller a) den andre veien:
def skriv_resultatliste(tabell):
for rad in tabell:
print(rad[0].ljust(14) + rad[1].ljust(10) + sekunder_til_tid(rad[2]))Utskrift: ingen — blokken definerer bare funksjonen. Den skriver først noe når den blir kalt.
Deloppgave d) — main, som leser fila én gang:
def main(filnavn):
tabell = les_stevne(filnavn)
skriv_resultatliste(tabell)Kaller du main('stevne.txt') etter at de fire funksjonene over er definert, skriver programmet:
Utskrift:
Ida Nervik Havorn 1:04.32
Jonas Lie Delfin 0:58.91
Amina Kaur Havorn 1:12.07
Petter Ruud Storm 1:04.09Slik ville sensor sett på det: tid_til_sekunder kalles fra innlesingen, sekunder_til_tid fra utskriften, og ingen av de to regnestykkene står skrevet mer enn ett sted. Det er gjenbruk i praksis, og det er verdt poeng i hver eneste deloppgave som bruker det.
Legg også merke til at tabellen inneholder tall, ikke tidsstrenger, hele veien fra innlesingen til utskriften. Konverteringen tilbake til tekst skjer først i det siste øyeblikket, når noe skal vises for et menneske. Det er en god regel i alle programmer: regn med tall, vis tekst.
differanse(tabell) som returnerer differansen mellom den raskeste og den treigeste tiden, som en tidsstreng på formen minutter:sekunder.hundredeler.»Funksjonen skal gjenbruke sekunder_til_tid.
maalinger.log har én linje per avlesning, med feltene skilt av et varierende antall mellomrom: dato, forbruk i liter og målerplassering. Forbruket kan være tomt når avlesningen mislyktes.(a) Skriv les_logg(filnavn) som returnerer en 2D-liste der forbruket er heltall, eller None når feltet mangler.
(b) Skriv forbruk_per_maaned(tabell) som returnerer {maaned: sum liter}, der måneden er de sju første tegnene i datoen ('2026-02'). Avlesninger uten forbruk skal ikke telle med.»
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.