Prøveeksamen

Spørsmålene som pdf (uten løsningsforslag) finner du her. Tilgjengelig under prøveeksamen og eksamen er kursnotatene som pdf. Internett og kodeeditor er ikke tilgjengelig utover det man har tilgang til via Safe Exam Browser (SEB) og Inspera. Forøvrig er alle skriftlige/trykte hjelpemidler tillatt, og varighet er 4 timer.

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

  1. Automatisk rettet
  2. Forklaring
  3. Kodeskriving

1 Automatisk rettet

a = "Hello, how are you!"
b = { 3, 5, 1 }
c = -1.4
d = 64
e = [ 42, "Bye", -3 ]

Velg datatype til uttrykket.

int bool list float str (-error-)
[b]
b+d
len(b)
e*d
a*d
f'{b}'
a+a
3 in e
c*d
a[e[2]]

int bool list float str (-error-)
[b]
b+d
len(b)
e*d
a*d
f'{b}'
a+a
3 in e
c*d
a[e[2]]

a = [1, 0, -2, 2, 5, 3]

# Gitt at koden over er kjørt.
# Hva skrives ut i de følgende setningene?
# (hvis programmet krasjer, skriv kun 'Error')
# Klikk 'kjør' for å se svaret

print(a[4])
print(a[3 - 1])
print(a[3] - 1)
print(a[a[-1]-1])
print(a[a[0] - a[1]])
print(a[a[a[0]]])

Velg slik at alle sammenligningene er True. Dict’et xs ser slikt ut:

xs = {
    'a' : 5,
    '5' : 'hello',
    7 : 9.3781,
    5 : [7, 'Bergen', {3,5}],
}

== ‘Bergen’

5 in

!= 12

== xs[‘a’]

xs = {
    'a' : 5,
    '5' : 'hello',
    7 : 9.3781,
    5 : [7, 'Bergen', {3,5}],
}

print(xs[5][1] == 'Bergen')
print(5 in xs.keys())
print(xs[5][1] * 2 != 12)
print(len(xs['5']) == xs['a'])

Velg slik at alle sammenligninger blir True. Sets xs og ys ser slikt ut:

xs = set("Welcome")
ys = set("Velkommen")

== set(“elom”)

set(“Welcom”) ==

== xs.union(ys)

== 7

xs = set("Welcome")
ys = set("Velkommen")

print(xs.intersection(ys) == set("elom"))
print(set("Welcom") == xs)
print(set("WVelkcomn") == xs.union(ys))
print(len(ys) == 7)

a b c (a and b) or c
True False False
True True False
True False True
False False True

a b c (a and b) or c
True False False False
True True False True
True False True True
False False True True

Skriv en funskjon som tar inn en liste med binærtall 0 og 1, og returnerer True hvis kun det første tallet i listen er 1. Ellers returnerer funksjonen False.

(fyll inn stedene hvor det står ...)

def only_first_is_one(list_of_digits):
    x = list_of_digits[0]
    if ...:
        return ...
    for d in list_of_digits[1:]:
        if ...:
            ...
    return True

def only_first_is_one(list_of_digits):
    x = list_of_digits[0]
    if x == 0:
        return False
    for d in list_of_digits[1:]:
        if d == 1:
            return False
    return True

print("Tester only first_is_one...", end=" ")
assert only_first_is_one([1, 0, 0, 0])
assert only_first_is_one([1])
assert not only_first_is_one([1, 0, 0, 1, 0])
assert not only_first_is_one([0, 0, 0, 0, 0])
print("OK")

Hvordan plassere parenteser for å få et uttrykk identisk med

  • 12 // 2 * 3
  • x and y or z in a

  • (12 // 2) * 3

  • (x and y) or (z in a)

Hva skriver dette programmet ut? (hvis programmet krasjer, skriv kun ‘Error’)

b = 3
a = a * b
b = a + b
a -= 1
print(b - a)

Kjør programmet for å se fasit.

Det var en feil i den automatiske rettingen av denne oppgaven. Oppgaven skulle egentlig ha sett slik ut (da ville autorettingen fungert):

a = 2
b = 3
a = a * b
b = a + b
a -= 1
print(b - a)

Hva skriver dette programmet ut? (hvis programmet krasjer, skriv kun 'Error')
def foo(x):
    x += 2
    return x
x = 10
y = foo(x)
print(x + y)

Kjør programmet for å se fasit.

Hva skriver dette programmet ut? (hvis programmet krasjer, skriv kun 'Error')
def woz(s):
    s += 'buzz'
    print(s, end=' ')
s = 'fizz'
r = woz(s)
print(f'{s} {r}')

Kjør programmet for å se fasit.

Hva skriver dette programmet ut? (hvis programmet krasjer, skriv kun 'Error')
def qiz(x, a):
    for e in a:
        if x % 2 == 0:
            x += e
    return x
q = [2, 4, 5, 6]
print(qiz(0, q))

Kjør programmet for å se fasit.

Hva skriver dette programmet ut? (hvis programmet krasjer, skriv kun 'Error')
def charlie(p, q):
    r = p - q
    s = delta(r, p)
    s = delta(s, q)
    return s
def delta(t, u):
    v = t + u
    return v - 1
print(charlie(5, 2))

Kjør programmet for å se fasit.

Hva skriver dette programmet ut? (hvis programmet krasjer, skriv kun 'Error')
def foxtrot(x):
    if x >= 10:
        return 10
    else:
        x += 10
    if x > 10:
        if x % 2 == 0:
            x += 1
        elif x >= 15:
            x -= 1
    else:
        return 42
    
    return x - 10

print(foxtrot(0))
print(foxtrot(2))
print(foxtrot(3))
print(foxtrot(5))
print(foxtrot(foxtrot(2)))

Kjør programmet for å se fasit.

Det var en feil i oppgaveteksten for den siste deloppgaven, da den i Inspera manglet den siste lukke-parentesen. Derfor var egentlig Error riktig svar på siste deloppgave; men vi aksepterer både Error og 3 som riktig svar.


2 Forklaring
(a) Forklar feilen (10 poeng)

def count_a(s):
    for c in range(len(s)):
        count = 0
        if c == "a":
            count += 1
        return count

Koden over skal telle hvor mange ganger tegnet "a" opptrer i en streng s, men gir feil svar. Forklar hva som er feil, og hva man kan gjør for å fikse funksjonen.

  • 3 poeng: count = 0 må flyttes utenfor løkken.

  • 2 poeng: Løkken blander sammen indekser og elementer i strengen.

  • 3 poeng: For tidlig return

  • 2 poeng: Skjønnsmessig vurdering. Bruker kandidaten gode faguttrykk? Er det sammenheng mellom resonnementer?

Koden over har flere feil.

  • Løkken blander indekser med tegn i strengen. Løkken er over indekser (den bruker range(len(...))), men iteranden c blir brukt som om den er et symbol i strengen (f. eks. c == "a"). For å reparerer dette, må vi endre for c in range(len(s)): til for c in s:.

  • count blir satt til 0 på nytt hver gang blokken i løkken kjører. Dette er fordi count blir definert inni løkken. For å reparerer dette, må vi flytte count = 0 utenfor løkken (før løkken starter).

  • return count blir kjørt allerede etter første gjennomgang av løkken, og kodeblokken i løkken utføres derfor bare én gang. Når return -setningen utføres inne i en løkke, avbrytes løkken og funksjonskallet med én gang. For å reparerer dette, må vi endre innrykket og flytte return count utenfor løkken (etter at løkken er ferdig).

def count_a(s):
    count = 0
    for c in s:
        if c == "a":
            count += 1
    return count

print(count_a("banan"))

(b) Destruktive og ikke-destrukive funksjoner i snake (10 poeng)

Forklar forskjellen på en destruktiv og en ikke-destruktiv funksjon. Vis til eksempler på begge deler i det vedlagte løsningsforslaget til lab6.

  • 2 poeng: en destruktiv funksjon muterer/endrer på en verdi gitt som argument til funksjonen. Denne endringen vil påvirke variablene hos den som kaller på funksjonen også.

  • 2 poeng: Korrekt eksempel på minst én destruktiv funksjon i snake

  • 2 poeng: Korrekt eksempel på minst én ikke-destruktiv funksjon i snake

  • 4 poeng: Skjønnsmessig vurdering. Bruker kandidaten gode faguttrykk? Er det sammenheng mellom resonnementer?

En destruktiv funksjon er en funksjon med sideeffekter: den muterer minst ett av argumentene som blir gitt til funksjonen. I snake.py er for eksempel funksjonen subtract_one_from_all_positives en destruktiv funksjon, siden den muterer listen den blir gitt som argument. Selv om denne funksjonen ikke har noen retur-verdi, har den en sideeffekt: den trekker fra 1 på mange posisjoner i en 2d-liste. Denne endringen kan observeres etter at funksjonskallet er ferdig, og funksjonen er derfor destruktiv. Andre destruktive funksjoner i snake.py som er gode eksempler: add_apple_at_random_location, move_snake, key_pressed, timer_fired, init, og app_started

En ikke-destruktiv funksjon er en funksjon uten sideeffekter: den eneste måten man kan ha utbytte av å kalle på en ikke-destruktiv funksjon er ved å benytte seg av retur-verdien til funksjonen. Ikke-destruktive funksjoner har den fordelen at de er lettere å feilsøke og ressonere rundt. Gode eksempler på ikke-destruktive funksjoner i snake.py er new_default_board, get_max_value_in_2dlist, position_of_value_in_2dlist, get_next_head_position, is_legal_move, og get_color.

(c) Forklar oppslagsverk (10 poeng)

Lille Tidemann (10) skal snart ha eksamen i INF100, men skjønner ikke helt hva et oppslagsverk (dict) er. Skriv en innføring som hjelper ham. Bruk forklaringer med ord sammen med illustrerende eksempler på kode. Inkluder ditt eget eksempel på en realistisk situasjon der det er lurt å bruke et oppslagsverk for å løse oppgaven.

Vi forventer ca 250 ord.

  • 3 poeng: eksempel som viser hvordan man gir verdi til en nøkkel eller henter ut verdien til en nøkkel.

  • 3 poeng: fornuftig (semi-realistisk) eksempel

  • 4 poeng: Skjønnsmessig vurdering. Bruker kandidaten gode faguttrykk? Er det sammenheng mellom resonnementer?

Et oppslagsverk er en samling av nøkler som hver har en verdi knyttet til seg. Vi kan tenke på et oppslagsverk som en slags telefonkatalog, der vi kan raskt bla oss frem til et navn (en nøkkel) og så finne telefonnummeret (en verdi) som er knyttet til navnet.

I python kan vi opprett et oppslagsverk på denne måten:

phone_catalog = {
    'Kari Nordmann': 99887766,
    'Ola Nordmann': 55443322,
    'Lille Tidemann': 11223344,
}

Hvis vi ønsker å vite hva telefon-nummeret til Ola Nordmann er, kan vi deretter hente det ut på denne måten:

olas_number = phone_catalog['Ola Nordmann']
print(olas_number) # skriver ut 55443322

Hvis vi vil legge til en ny person i telefonkatalogen/oppslagsverket (eller endre telefonnummeret til en eksisterende person), kan vi gjøre det slik:

phone_catalog['Silje Nyborger'] = 11199111

Vi kan tenke på et oppslagsverk som en samling av «variabler», der nøkkelen er variabelnavnet. Variabler peker på verdier, og én nøkkel kan kun peke på én verdi om gangen, akkurat på samme måte som én variabel peker på én verdi på et gitt tidspunkt.

Vi kan også tenke på et oppslagsverk som om det var en litt spesiell liste, men hvor man benytter nøkler i stedet for indekser for å angi hvilken posisjon man vil se på eller endre på. På samme måte som en liste, kan et oppslagsverk muteres, så man må for eksempel vite at man ikke ødelegger for noen andre hvis man endrer på et oppslagsverk man får gjennom en funksjonsparameter.


3 Kodeskriving
(a) Opprett et alias (3 poeng)

Illustrasjon av minnet

Opprett to variabler a, og b som refererer til den samme listen, slik at minnets tilstand blir som vist på bildet over

  • 1 poeng dersom a og b er aliaser

  • 1 poeng hvis a er en liste med elementene 1, 2, og 3.

  • 1 poeng hvis løsningen er helt korrekt og i tillegg ikke er unødvendig komplisert

# Klikk på 'se steg' for å verifisere at vi får riktig bilde.
a = [1, 2, 3]
b = a

(b) Opprett lister og referanser (4 poeng)

Illustrasjon av minnet

Opprett tre variabler a, b og c, slik at minnets tilstand blir som vist på bildet over.

  • 1 poeng hvis a er en liste med 1, 2, 3

  • 1 poeng hvis a er et element i b

  • 1 poeng hvis c er riktig

  • 1 poeng hvis alt over er riktig, og løsningen i tillegg ikke er unødvendig komplisert

# Klikk på 'se steg' for å verifisere at vi får riktig bilde.
a = [1, 2, 3]
b = [a, 2, 3]
c = [a, b, 3]

(c) Nyttårsløpet 2017 (20 poeng)

I denne oppgaven skal du lage fem funksjoner som del av et rapporteringssystem fra et idrettsstevne «Nyttårsløpet 2017» som finner sted 31. desember 2017.

Funksjonene skal operere på en liste av deltakere. Hver deltaker i listen er igjen representert som en liste av 5 elementer: en streng som inneholder fornavn, en streng som inneholder etternavn, en bokstav som representerer kjønn (‘K’ for kvinne og ‘M’ for mann), et heltall som er fødselsåret, og en streng som representerer tiden vedkommende klarte å gjennomføre en halvmaraton på. (‘1:59:20’ betyr 1 time, 59 minutter og 20 sekunder.) Et eksempel på en slik liste av lister er som følger:

data = [
    ['Kari', 'Hansen', 'K', 1969, '1:59:20'],
    ['Eli', 'Nansen', 'K', 1975, '1:49:46'],
    ['Karl', 'Jansen', 'M', 1985, '1:35:40'],
    ['Erik', 'Karlsen', 'M', 1970, '1:40:48'],
    ['Anne', 'Jensen', 'K', 1964, '2:03:09'],
    ['Kurt', 'Johnsen', 'M', 1987, '1:32:43'],
    ['May', 'Berntsen', 'K', 1989, '1:36:54'],
    ['Jan', 'Thorsen', 'M', 1990, '1:45:24'],
    ['Hans', 'Monsen', 'M', 1998, '1:25:05'],
]

Som del av rapporteringssystemet skal du lage følgende fem Python-funksjoner.

Funksjonen time er en hjelpefunksjon til winners.

group

Parametre:

  • data en liste av lister som representerer resultatene fra et idrettsstevne som beskrevet over.
  • gender en streng som enten er 'K' eller 'M'.
  • age_lower en int som representerer nedre aldersgrense (inklusiv)
  • age_upper en int som representerer øvre aldersgrense (eksklusiv)

Returverdi: ingen

Sideeffekter:

  • Alle deltakere i data som har kjønn gender og som har alder mellom age_lower og age_upper på arrangementsdagent skal skrives ut på formatet fornavn etternavn (alder) tid på hver sin line.

Eksempelutskrift for et kall til group(data, 'K', 25, 50) gitt data som beskrevet over:

Kari Hansen (48) 1:59:20
Eli Nansen (42) 1:49:46
May Berntsen (28) 1:36:54

Parametre:

  • data en liste av lister som representerer resultatene fra et idrettsstevne som beskrevet over.
  • word en streng som skal søkes etter.

Returverdi:

  • en liste som representerer deltakerne fra data hvor word finnes i enten fornavnet eller etternavnet til deltakeren. Formatet på resultatlisten skal være akkurat den samme som for data, altså en liste av lister.

Sideeffekter: ingen

Eksempel på tester:

# Test 1
actual = search(data, 'Hans')
expected = [
    ['Kari', 'Hansen', 'K', 1969, '1:59:20'],
    ['Hans', 'Monsen', 'M', 1998, '1:25:05'],
]
assert expected == actual

# Test 2
actual = search(data, 'M')
expected = [
    ['May', 'Berntsen', 'K', 1989, '1:36:54'],
    ['Hans', 'Monsen', 'M', 1998, '1:25:05'],
]
assert expected == actual
winners

Parametre:

  • data en liste av lister som representerer resultatene fra et idrettsstevne som beskrevet over.

Returverdi: ingen

Sideeffekter:

  • Den raskeste mannen og den raskeste kvinnen skal skrives ut på hver sin linje på formatet vist i eksempelet under.

Eksempelutskrift for et kall til winners(data) gitt data som i eksempelet over:

The fastest woman is May Berntsen with time 1:36:54
The fastest man is Hans Monsen with time 1:25:05
time

Parametre:

  • s en streng på formatet H:MM:SS som representerer en tid hvor H er antall timer, MM er antall minutter og SS er antall sekunder.

Returverdi:

  • En int for antall sekunder som tilsvarer tiden s.

Sideeffekter: ingen

Dersom s ikke er på riktig format skal funksjonen krasje.

Eksempel på test:

actual = time("1:59:20")
expected = 7160
assert expected == actual
save_file

Parametre:

  • data en liste av lister som representerer resultatene fra et idrettsstevne som beskrevet over.
  • filename en streng som representerer filnavnet som resultatene skal lagres i.

Returverdi: ingen

Sideeffekter:

  • Resultatene i data skal lagres i filen filename i et CSV-format hvor verdier separeres med semikolon. Første linje skal inneholde egnede overskrifter. De neste radene i filen skal inneholde fornavn, etternavn, kjønn, fødselsår og tid for hver av deltakerne.

Alle poeng tildeles ved skjønssmessig vurdering.

  • 4 poeng: group
    • 1 poeng: løkke over data
    • 1 poeng: hente ut fødselsår for elementene i løkken
    • 1 poeng: meningsfull betingelse
    • 1 poeng: helhetsvurdering
  • 4 poeng: search
    • 1 poeng: løkke, hente ut årstall for person og regne ut alder
    • 1 poeng: bruk av append eller tilsvarende på resultat-liste
    • 1 poeng: meningsfull betingelse uten presedensfeil
    • 1 poeng: helhetsvurdering
  • 4 poeng: winners
    • 1 poeng: filtrerer på kjønn
    • 1 poeng: finner raskeste
    • 2 poeng: helhetsvurdering
  • 4 poeng: time
    • 1 poeng: isolerer timer, minutter, sekunder i streng
    • 1 poeng: konverterer til int
    • 1 poeng: regnestykke er rimelig
    • 1 poeng: helhetsvurdering
  • 4 poeng: save_file
    • 1 poeng: åpner fil på fornuftig måte
    • 1 poeng: header
    • 1 poeng: løkke
    • 1 poeng: helhetsvurdering

# Del 1
def group(data, gender, age_lower, age_upper):
    for p_first, p_last, p_gender, p_birth_year, p_time in data:
        age = 2017 - p_birth_year
        if (gender == p_gender) and (age_lower <= age < age_upper):
            print(f'{p_first} {p_last} ({age}) {p_time}')
        
# Del 2
def search(data, word):
    result = []
    for p in data:
        p_first, p_last, *__ = p
        if (word in p_first) or (word in p_last):
            result.append(p)
    return result

# Del 3
def winners(data):
    woman_winner = get_fastest(data, 'K')
    first, last, *__, time = woman_winner
    print(f'The fastes woman is {first} {last} with time {time}')

    man_winner = get_fastest(data, 'M')
    first, last, *__, time = man_winner
    print(f'The fastes man is {first} {last} with time {time}')


def get_fastest(data, gender):
    best_person = []
    best_time = None
    for person in data:
        *__, p_gender, __ , p_time = person
        if p_gender != gender:
            continue

        seconds = time(p_time)
        if (best_time is None) or (seconds < best_time):
            best_time = seconds
            best_person = person

    return best_person

# Del 4
def time(s):
    h, mm, ss = s.split(':')
    return int(h) * 60 * 60 + int(mm) * 60 + int(ss)

# Del 5
def save_file(data, filename):
    with open(filename, 'wt', encoding='utf-8') as f:
        f.write('fornavn;etternavn;kjønn;fødselsår;tid\n')
        for p in data:
            f.write(';'.join([str(x) for x in p]))
            f.write('\n')

(d) Hvor starter lengste serie (10 poeng)

Anta at a er en liste med heltall (int). Vi sier at en sammenhengende subsekvens av etterfølgende tall i listen a utgjør en serie dersom alle tallene er positive, eller alle tallene er negative. Tallet 0 er hverken positivt eller negativt, og kan ikke inngå i noen serie.

Eksempel:

a = [5, 0, 1, 3, 6, 4, 0, 0, 3, 3, 1, -7, -8, -2]

De «maksimale» seriene (de seriene som ikke kan utvides videre) i listen a over er:

  • 5
  • 1, 3, 6, 4
  • 3, 3, 1
  • -7, -8, -2

Skriv en funksjon longest_series_start med en parameter a. La funksjonen returnere indeksen hvor den lengste serien i a begynner. I eksempelet over skal altså returverdien bli 2, siden serien (1, 3, 6, 4) begynner på indeks 2 og dette er den lengste serien i listen.

Hvis det er flere enn én slik lengste serie, skal den tidligste av dem returneres. Hvis det ikke er noen serier i a, skal verdien -1 returneres.

  • 2 poeng: bruke løkke som leter etter endepunkt for serie

  • 2 poeng: system for å ta huske på lengste serie

  • 2 poeng: identfiserer hva som er en gyldig serie på meningsfull måte

  • 1 poeng: god sjekk om to verdier har samme fortegn

  • 3 poeng: skjønnsmessig vurdering

def has_same_sign(x, y):
    if x < 0 and y < 0:
        return True
    if x > 0 and y > 0:
        return True
    return False

def get_end_of_series_starting_at(a, start_i):
    start_val = a[start_i]
    if start_val == 0:
        return -1

    i = start_i
    while i < len(a) and has_same_sign(start_val, a[i]):
        i += 1
    return i

def longest_series_start(a):
    longest_start_i = -1
    longest_length = 0

    start_i = 0
    while start_i < len(a):
        end_i = get_end_of_series_starting_at(a, start_i)
        length = end_i - start_i
        if length > longest_length:
            longest_start_i = start_i
            longest_length = length

        start_i += max(length, 1) # kun += 1 funker også, men dette er raskere

    return longest_start_i

print("Test longest_series_start...", end=" ")
a = [5, 0, 1, 3, 6, 4, 0, 0, 3, 3, 1, -7, -8, -2]
assert 2 == longest_series_start(a)

a = [5, 0, 1, 3, 6, 4, 0, 0, 3, 3, 1, -7, -8, -2, -9, -1]
assert 11 == longest_series_start(a)

a = [5, 1, 3, 0, 6, 4, 3, 0 ]
assert 0 == longest_series_start(a)

a = [0, 0, 0]
assert -1 == longest_series_start(a)

a = [0, 0, 0, 9]
assert 3 == longest_series_start(a)
print("OK")