Prøveeksamen

10. november 2025
4 timer
Lukket digital eksamen (med safe exam browser).
Tillatte hjelpemiddel: bøker frå litteraturlista og opp til 6 tosidige A4 ark med eigne notat.
 

Oppgavetekster, løsningsforslag og sensorveiledning finner du på denne siden.

  1. Automatisk rettet
  2. Forklaring
  3. Kodeskriving

Fordi eksamen var en lukket digital eksamen uten tilgang til å kjøre koden eller bruke internett, bes sensor ikke gi poengtrekk for forhold som enkelt ville blitt oppdaget og raskt rettet ved kjøring av koden. Dette inkluderer blant annet:

  • manglende import-setninger,
  • manglende kodeord (som f. eks. manglende def foran funksjonsdefinisjoner),
  • feil navn på funksjoner og metoder i standardbiblioteket, i egen kode eller i eksterne moduler (såfremt det fremgår noenlunde av funksjonsnavnet hva kandidaten egentlig mener),
  • feil navn på variabler (f. eks. kalle den samme variabelen både total og sum i ulike deler av koden),
  • enkle syntaks-feil (f. eks. manglende kolon etter if-setninger),
  • og så videre.

Logiske feil skal det likevel (som hovedregel) bli trukket litt for; selv om man kunne ha oppdaget at noe var feil ved kjøring av koden. Dette inkluderer blant annet:

  • presedensfeil,

  • forveksling av indekser og elementer,

  • off-by-one -feil,

  • feil i algoritmer,

  • og så videre.


1 Automatisk rettet

Deloppgaver a-h: oppgaver, fasit

Oppgave 1(i) og 1(j) ba kandidatene om å skrive korte kodesnutter. Noen tester var oppgitt, og man kunne teste/kjøre koden sin underveis på eksamen i disse oppgavene. Alle testene må passeres for å få poeng, men kandidatene får vite umiddelbart om koden man skrev passerer de oppgitte testene eller ikke.

Skriv en funksjon leave_numbers_and_dots som aksepterer en streng som argument og returnerer en ny streng som bare inneholder tallene og punktumene fra den opprinnelige strengen.

Test case # Input Forventet output
1 leave_numbers_and_dots(‘60g.2erg3.g3frged3’) 60.23.33
2 leave_numbers_and_dots(‘se17vernajast0l3ca’) 1703
3 leave_numbers_and_dots(‘velfr.vberger.evf.4jfvb2’) …42

Startkode:

def leave_numbers_and_dots(text):
    ... # TODO: write the function body
    
    
    
# DO NOT EDIT THE LINE BELOW, OTHERWISE THE AUTOTEST WILL NOT WORK
print(eval(input()))

def leave_numbers_and_dots(text):
    result = ''
    for c in text:
        if c in '0123456789.':
            result += c
    return result

Skriv en funksjon get_file_format som aksepterer en filnavnstreng som et argument og returnerer en meldingsstreng som beskriver filtypen basert på filnavnets endelse.

Funksjonen skal gjenkjenne .txt og .png-filer, og ellers returnere en standardmelding.

Test case # Input Forventet output
1 get_file_format(“IMG3906.png”) This is an image file
2 get_file_format(“README.txt”) This is a text file
3 get_file_format(“script.py”) Unknown
4 get_file_format(“weird.xtxt”) Unknown

Startkode:

# Din kode her:
def get_file_format(filename):
    ... # TODO: write the function body
    
    
    
# DO NOT EDIT THE LINE BELOW, OTHERWISE THE AUTOTEST WILL NOT WORK
print(eval(input()))

def get_file_format(filename):
    if filename[-4:] == '.png':
        return 'This is an image file'
    elif filename[-4:] == '.txt':
        return 'This is a text file'
    else:
        return 'Unknown'
2 Forklaring
2(a)  Iterasjon (8 poeng)

Mange objekter i Python kan «itereres» over. Men hva betyr det egentlig i praksis? Hvilke typer objekter tillater iterasjon, og hva er de praktiske eksemplene på iterasjonsanvendelse?

Skriv en forklaring som tydeliggjør disse ideene. Beskriv hva det betyr at et objekt er itererbart, hva som skjer når programmet kjører en løkke, og illustrer et eksempel på iterering av et virkelig problem når det løses i Python.

Forklaringen bør bestå av maksimalt 2–3 avsnitt.

En iterasjon er en kjøring gjennom kodeblokken i en løkke. Et iterabelt/itererbart objekt er et objekt man kan «gå gjennom» med en for-løkke. De mest prominente eksemplene på iterable objekter er strenger (en sekvens av bokstaver), range (en sekvens av tall), lister (en sekvens av objekter) og oppslagsverk (gir oss en sekvens av nøkler hvis vi itererer), men det finnes også mange andre (set, tuple, enumerate etc). Vi kan tenke på et iterabelt objekt som en sekvens av «under» -objekter.

En løkke over et iterabelt objekt ser slik ut:

for thing in iterable_object:
    ...

I eksempelet over er thing en iterand; altså en variabel som peker på ett av under-objektene som finnes inne i den iterable. Løkkekroppen (her angitt ved …) utføres én gang for hvert av under-objektene i den iterable, og i hver iterasjon av løkken endres iteranden til å peke på neste objekt fra den iterable.

Eksempel: la oss si at vi har en liste med studenter i et emne, og du ønsker å telle hvor mange av dem som heter Jonas. Da kan vi bruken en løkke for å gå gjennom hvert element i listen, og telle dersom vedkommende heter Jonas.

names = ['Jonas', 'Erna', 'Ine', 'Jonas', 'Jens']
count = 0
for name in names:
    if name == 'Jonas':
        count += 1
print(count) # 2

TBA

2(b)  Tidemann sin Snake (10 poeng)

En student jobber med lab5 (Snake), men har gjort en feil i trinnet der et rutenett tegnes. Når testprogrammet view_test.py kjøres, vises programmet til venstre, mens det skal se ut som programmet til høyre. Det er ingen feilmeldinger i terminalen.

Tidemann sin Snake
Riktig Snake

Hva er gjort feil? Les koden, og forklar:

  • hva feilen består i,
  • hvorfor feilen fører til den viste oppførselen,
  • hva som kan gjøres for å rette opp feilen.

Det er flere feil:

  1. I snake_view.py på linje 11 i funksjonen draw_board er det oppgitt at løkken skal gå gjennom elementene i range(rows). Dette fører til at det blir tegnet for få kolonner i rutenettet. For å rette feilen, endre linje 11 slik at løkken går igjennom elementene i range(cols) i stedet.

  2. I snake_view.py, på linje 32-33 i funksjonen get_color angis det at fargen skal være oransje dersom den innkommende verdien er større enn eller lik 0. Dette fører til at funksjonen vil returnere fargen oransje selv om verdien er 0, som ikke er riktig oppførsel, og vi ser på bildet at hele brettet tegnes i den oransje fargen, også der det skulle vært grå. Dette problemet kan løses på (minst) tre ulike måter, og ideelt sett burde man gjøre alle disse rettelsene (selv om hvert enkelt grep i seg selv er egentlig er tilstrekkelig for rette funksjonaliteten):

    • I stedet for å sammenligne med >=, burde man sammenlignet med >. Da ville ikke kroppen til denne if-setningen bli utført, og det er istedet den lysegrå fargen som blir angitt til color-variabelen tidligere i funksjonen (linje 30-31) som gjør seg gjeldende.
    • I stedet for å angi verdi til en variabel color som returneres på slutten av funksjonen, kunne man returnert verdier direkte inne i if-blokkene. Da ville funksjonen returnert allerede i den første if-blokken.
    • I stedet for å benytte flere if-setninger, burde man benyttet seg av én if-setning med flere elif-ledd; altså endre if på linje 32 til elif. Da ville color-variabelen blitt angitt kun i det første leddet av if-elif-sekvensen.
  1. I snake_view.py, på linje 22-26 i funksjonen draw_board tegnes debug-informasjon for en celle i en if-setning; men denne if-setningen har et innrykk for lite, og utføres derfor kun én gang for hver rad, etter at den innerste løkken er ferdig utført. Variablene x_left, x_right, y_top og y_bottom vil fortsatt ha de verdiene de fikk i siste iterasjon av den innerste løkken, som er ansvarlig for å tegne siste kolonne i raden. Derfor tegnes debug-informasjonen kun for den siste kolonne. For å rette koden, må innrykket på linjene 22-26 økes med ett innrykk (fire mellomrom).
  • For få kolonner (2 poeng)
    • Identifiserer at dette er et problem.
    • Foreslår funksjonell løsning for hva som kan gjøres for å rette problemet.
  • For mye oransje (2 poeng)
    • Identifiserer at dette er et problem.
    • Foreslår funksjonell løsning for hva som kan gjøres for å rette problemet.
  • Debug vises kun for siste kolonne (2 poeng)
    • Identifiserer at dette er et problem.
    • Foreslår funksjonell løsning for hva som kan gjøres for å rette problemet.
  • Helhetsvurdering (4 poeng)
    • Meningsfulle forklaringer av sammenheng mellom kode og resultatbilde.
    • Gode bruk av faguttrykk.
    • Finner ikke feil som ikke er feil.
3 Kodeskriving
3(a)   Minnets tilstand (8 poeng)

Skriv en kodesnutt slik at minnets tilstand blir som vist under. Du vil ikke få trekk i poeng dersom du definerer andre variabler i tilegg.

Minnets tilstand

Klikk på «se steg» -knappen for å verifisere at denne koden gir riktig bilde av minnet.

a = [1, 2, 3]
b = [a, 2, 3]
c = [a, b, 3]

TBA

3(b)  Varighet (15 poeng)

Filen records.csv er en kommaseparert fil som har tre kolonner med overskrifter:

  • start – float – tidspunktet observasjonen startet, i sekunder
  • end – float – tidspunktet observasjonen sluttet, i sekunder
  • label – én bokstav – typen observasjon.

I denne oppgaven skal du skrive et lite program som leser inn records.csv, beregner den totale observerte varigheten per label (varighet = end − start), og skriver en ny CSV-fil durations.csv som skal inneholde nøyaktig to kolonner: label og total_duration. Eksempler på gitt og ønsket CSV-fil:

Eksempel records.csv

start,end,label
0.00,2.10,A
2.20,4.50,B
4.60,7.00,B
7.10,9.20,C
9.30,12.00,C
12.10,14.00,A

Eksempel durations.csv

label,total_duration 
A,4.0
B,4.7 
C,4.8
from pathlib import Path
from io import StringIO
from csv import DictReader, DictWriter

def main():
    data = read_csv('records.csv')
    total_durations = get_total_durations(data)

    # Antar fra nå at total_durations er et oppslagsverk på formen
    # {
    #    "A": 4.0,
    #    "B": 4.7,
    #    "C": 4.8
    # }

    new_headers = ['label', 'total_duration']
    new_data = []
    for label in total_durations:
        row = {
            'label': label,
            'total_duration': total_durations[label]
        }
        new_data.append(row)

    write_csv('durations.csv', new_headers, new_data)


def get_total_durations(data):
    tot_durations = {}
    for row in data:
        label = row['label']
        if label not in tot_durations:
            tot_durations[label] = 0
        this_duration = float(row['end']) - float(row['start'])
        tot_durations[label] += this_duration
    return tot_durations


def read_csv(filename):
    raw_content = Path(filename).read_text(encoding='utf-8')
    reader = DictReader(StringIO(raw_content))
    return list(reader)


def write_csv(filename, headers, data):
    output = StringIO()
    writer = DictWriter(output, fieldnames=headers, lineterminator='\n')
    writer.writeheader()
    writer.writerows(data)
    my_string = output.getvalue()
    Path(filename).write_text(my_string, encoding='utf-8')


if __name__ == '__main__':
    main()

TBA