Prøveeksamen

Spørsmålene (uten fasit) finner du som pdf.


Løsningsforslag og sensorveiledning


Oppgave 1: typer

0.6 poeng per riktig svar, totalt 6 mulige poeng.

a*b        ->    <class 'str'>
len(c)     ->    <class 'int'>
f"{c}"     ->    <class 'str'>
c == 10.3  ->    <class 'bool'>
a*d        ->    (-error-)
b*c        ->    <class 'list'>
a + "b"    ->    <class 'str'>
a+b        ->    (-error-)
a+a        ->    <class 'str'>
[a]        ->    <class 'list'>
Oppgave 2: telling

1 poeng per riktig svar, men 4 poeng dersom begge svar var riktige.

Løsningsforslag:

def count(xs, x):
    ct = 0
    for i in xs:
        if i == x:
            ct += 1
    return ct

assert(3, count([1, 2, 3, 4, 3, 2, 3, 2, 1], 2))
Oppgave 3: logiske operasjoner

0.5 poeng per riktig svar, totalt 4 mulige poeng.

a      b      c         a and (b or c)      a or (not b)
True   True   True      True                True
False  False  True      False               True
False  True   True      False               False
True   False  False     False               True
Oppgave 4: lister

1 poeng per riktig svar, totalt 4 poeng

xs = ["hallo", [12, 13, 14], False, 3, 1.3]

print(xs[0] == 'hallo')
print(13 == xs[1][1])
print(xs[-3] == False)
print(len(xs[1]) == 3)
Oppgave 5: oppslagsverk

1 poeng per riktig svar, totalt 4 poeng

xs = {
    'a': 5,
    '5': 'hello',
    'hello': 3.1415,
    7: 'a',
    '7': 0
}

print(xs['5'] == 'hello')
print(7 in xs.keys())
print(xs[xs['5']] == 3.1415)
print(len(xs['5']) == xs['a'])
Oppgave 6: 2d-liste

2 poeng for riktig svar

# Klikk "se steg" for å vise at dette er riktig bilde
# Kjør programmet for å se riktig svar.

a = [[1, 2], [3, 4]]
print(a[1])
Oppgave 7: presedens

4 poeng for riktig svar.

def question(a, b, c, y, z):
    # Oppgave: sett paranteser slik at uttrykket bli identisk med
    return a or b and y < z or c

def answer(a, b, c, y, z):
    # Fasit:
    return (a or (b and (y < z))) or c
Oppgave 8: alias
# Klikk på 'se steg' for å verifisere at vi får riktig bilde.
a = [1, 2, 3]
b = a
Oppgave 9: liste med liste med liste
# Klikk på 'se steg' for å verifisere at vi får riktig bilde.
a = [1, 2, 3]
b = [a, 2, 3]
c = [a, b, 3]
Oppgave 10: prosentvis endring
x = 100

for _ in range(3):
    x = x * 1.5
    x = x * 0.5

print(x)
Oppgave 11: destruktive funksjoner i snake

Eksempelbesvarelse:

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.

Oppgave 12: feil i funksjon som finner to like på rad
def has_consecutive_elements(a):
    for i in a:
        if a[i] == a[i+1]:
            return True
        else
            return False

assert(has_consectutive_elements([1, 3, 3, 4]))
assert(not has_consectutive_elements([1, 3, 4, 3]))
assert(not has_consectutive_elements([3, 1, 4, 3]))
assert(has_consectutive_elements([3, 3, 1, 4]))
assert(has_consectutive_elements([1, 4, 3, 3]))

Eksempelbesvarelse:

Koden over har flere feil.

  • Det er en syntaksfeil på linje 5. Det mangler kolon etter else.
  • Koden returnerer for tidlig. Dersom det ikke stemmer at de to første elementene er like, vil funksjonen alltid returnere False. Så fort koden utfører return-setningen, avbrytes nemlig løkken og funksjonen er ferdig. Vi kan derfor ikke returnere False inne i løkken, siden vi ikke vet om svaret er False før vi har sjekket alle posisjoner. For å fikse problemet, kan vi flytte return False ut av løkken og heller ha den som siste setning i funksjonen etter at løkken er ferdig.
  • Koden blander indekser og elementer i løkken over a. Iteranden i brukes som om det er en indeks (f. eks. a[i] og a[i+1]), men i denne typen for-løkke er iteranden et element i listen a. For å bruke en løkke over indekser, endre løkken slik at den er over range(len(a)) i stedet for å være over a.
  • Koden krasjer fordi man forsøker å sammenligne a[i] og a[i+1] også når i er den siste posisjonen i listen. For å reparere dette, kan vi endre løkken slik at den slutter når i peker på nest siste posisjon. Løkken blir da over range(len(a) - 1).
Oppgave 13: tegne en stige
from uib_inf100_graphics import *

def draw_ladder(canvas, x_left, y_top, x_right, y_bottom, steps, col):
    # Strekene på sidene
    canvas.create_line(x_left, y_top, x_left, y_bottom, fill=col, width=5)
    canvas.create_line(x_right, y_top, x_right, y_bottom, fill=col, width=5)

    # Trinnene på stigen
    y_dist = (y_bottom - y_top)/(steps+1)
    for i in range(steps):
        y = y_top + (i + 1)*y_dist
        canvas.create_line(x_left, y, x_right, y, fill=col, width=5)    
    
def redraw_all(app, canvas):
    draw_ladder(canvas, 100, 50, 200, 350, 6, "red")

run_app(width=300, height=400)
Oppgave 14: whac-a-mole

Løsningsforslag (10 poeng)

# Her vises kun de funksjonene som måtte endres ift.
# kursnotatene sitt eksempel om klikking i rutenett.
# Med andre ord, i tillegg til denne koden; inkluder
# funksjonene point_in_grid, get_cell, get_cell_bounds
# fra kursnotatene.

def app_started(app):
    app.rows = 5
    app.cols = 8
    app.margin = 50 # margin rundt rutenettet

    app.mole_position = (-1, -1) # (row, col), men (-1,-1) betyr "ingen rute"
    app.mouse_is_over = (-1, -1) # (row, col), men (-1,-1) betyr "ingen rute"
    app.points = 0
    place_mole_randomly(app)

def place_mole_randomly(app):
    # velg en tilfeldig rute
    row = random.choice(range(app.rows))
    col = random.choice(range(app.cols))
    app.mole_position = (row, col)

def mouse_pressed(app, event):
    (row, col) = get_cell(app, event.x, event.y)
    if (row, col) == app.mole_position:
        app.points += 1
        place_mole_randomly(app)

def mouse_moved(app, event):
    app.mouse_is_over = get_cell(app, event.x, event.y)

def redraw_all(app, canvas):
    # poengsum
    canvas.create_text(app.width/2, 25,
                    text=f'Poeng: {app.points}',
                    font='Arial 20 bold')

    # tegn alle rutene
    for row in range(app.rows):
        for col in range(app.cols):
            (x0, y0, x1, y1) = get_cell_bounds(app, row, col)

            is_mole = (row, col) == app.mole_position
            is_mouse = (row, col) == app.mouse_is_over
            fill = get_color(is_mole, is_mouse)
            
            canvas.create_rectangle(x0, y0, x1, y1, fill=fill)

def get_color(is_mole, is_mouse):
    if is_mole and is_mouse: return 'light yellow'
    elif is_mole: return 'yellow'
    elif is_mouse: return 'gray60'
    else: return 'gray50'

run_app(width=400, height=300)
Oppgave 15: CSV-håndtering av fylker
import csv

def read_csv_file(path, encoding="utf-8", **kwargs):
    r''' Reads a csv file from the provided path, and returns its
    content as a 2D list. The default encoding is utf-8, the default
    column delimitier is comma and the default quote character is the
    double quote character ("), though this can be overridden with
    named parameters "delimiter" and "quotechar".'''
    with open(path, "rt", encoding=encoding, newline='') as f:
        return list(csv.reader(f, **kwargs))

adm_table = read_csv_file("NO_ADM12.csv", delimiter=";")

# Alternativ uten å bruke csv-modulen:
# with open("NO_ADM12.csv", "rt", encoding='utf-8') as f:
#     adm_table = [line.strip().split(";") for line in f.readlines()]

# Liste med oppslagsverk, ett oppslagsverk per kommune
municipalities = []
# Oppslagsverk, nøkkelen er fylkeskode, verdiene er navn på fylket
counties = {}

for row in adm_table[1:]:
    name = row[1]
    lat = float(row[3])
    lon = float(row[4])
    county_code = row[7]
    population = int(row[9])

    if row[5] == "ADM1":
        # Fylke
        counties[county_code] = name
    elif row[5] == "ADM2":
        # Kommune
        municipalities.append({
            "name": name,
            "county_code": county_code,
            "lat": lat,
            "lon": lon,
            "population": population,
        })

def get_municipalities_in(county_name, municipalities, counties):
    result = []
    for mun in municipalities:
        this_county_code = mun["county_code"]
        this_county_name = counties[this_county_code]

        if county_name == this_county_name:
        # Alternativ if-setning som ikke krever at man staver nøyaktig:
        # if this_county_name.lower().startswith(county_name.lower()):
            result.append(mun)
    return result

def get_largest(munlist):
    largest_size = -1
    largest = {}
    for mun in munlist:
        if mun["population"] > largest_size:
            largest_size = mun["population"]
            largest = mun
    return largest

def get_smallest(munlist):
    smallest_size = float("inf")
    smallest = {}
    for mun in munlist:
        if mun["population"] < smallest_size:
            smallest_size = mun["population"]
            smallest = mun
    return smallest

def get_northernmost(munlist):
    largest_lat = -float("inf")
    largest = {}
    for mun in munlist:
        if mun["lat"] > largest_lat:
            largest_lat = mun["lat"]
            largest = mun
    return largest

def get_southernmost(munlist):
    smallest_lat = float("inf")
    smallest = {}
    for mun in munlist:
        if mun["lat"] < smallest_lat:
            smallest_lat = mun["lat"]
            smallest = mun
    return smallest

def print_county(county_name, municipalities, counties):
    munlist = get_municipalities_in(county_name, municipalities, counties)
    if len(munlist) == 0:
        return False

    print("="*38)
    print(county_name)
    print("="*38)
    bigcity = get_largest(munlist)
    print(f'{bigcity["name"]:25} {bigcity["population"]:12}')
    smallcity = get_smallest(munlist)
    print(f'{smallcity["name"]:25} {smallcity["population"]:12}')
    northern = get_northernmost(munlist)
    print(f'{northern["name"]:20} {round(northern["lat"], 1):6}˚N '
        + f'{round(northern["lon"], 1):6}˚Ø')
    southern = get_southernmost(munlist)
    print(f'{southern["name"]:20} {round(southern["lat"], 1):6}˚N '
        + f'{round(southern["lon"], 1):6}˚Ø')
    print("="*38)
    return True

while True:
    print("Which county (q to quit)?", end=" ")
    county_name = input()
    if county_name == "q":
        break
    if not print_county(county_name, municipalities, counties):
        print("No matching county found. Try again.")