E-tenta 2023-12-02#

Information#

Tentamen består av två delar, del A och del B.

  • För betyg 3 krävs 6/10 på del A.

  • För betyg 4 krävs betyg 3 samt 4/11 på del B.

  • För betyg 5 krävs betyg 3 samt 8/11 på del B.

Del B rättas enbart om del A är godkänd.

Hjälpmedel#

Via den dator du skriver tentamen på har du åtkomst till:

Länkar till dessa hjälpmedel finns som flikar längst ned i Inspera.

Eftersom du har tillgång till en programmeringsmiljö kan du testa din kod innan du lämnar in den i Inspera. Du kopierar ditt svar från Programiz/Online Python/GDB Online till Inspera. Markera koden med CTRL-A, kopiera med CTRL-C och klistra in i svarsrutan i Inspera med CTRL-V. Ångra med CTRL-Z.

Medan du skriver tentamen sparas dina svar i Inspera var femtonde sekund. Kopiera ofta från online-IDE till Inspera för att inte riskera att förlora kod. Du kan inte öppna tentan igen när du har lämnat in den.

Bedömning#

I del A krävs genomgående att din kod ska fungera enligt anvisning. Kod som inte fungerar ges endast i undantagsfall något annat än 0 poäng.

I del B kan du få delpoäng för icke-fungerande kod som ändå visar att du kommit en bit på vägen.

Kommentering av kod behövs ej för del A. Uppgifterna på del B bör kommenteras på de ställen där en kommentar ökar läsbarheten markant. Svenska kommentarer är ok.

Såvida inget annat anges får du bygga på lösningar till föregående uppgifter även om du inte har löst dessa. Det är tillåtet att införa extra metoder eller funktioner. Uttryck som ”skriv en funktion som” ska alltså inte tolkas så att lösningen inte får struktureras med hjälp av fler funktioner. Alla uppgifter gäller programmeringsspråket Python och all programkod ska skrivas i Python. Koden ska vara läslig, dvs. den ska vara vettigt strukturerad och indenterad. Namn på variabler, funktioner, metoder, klasser etc. ska vara beskrivande men kan ändå hållas ganska korta.

A1#

Nedan är innehållet i en funktion. Den första raden (”funktionshuvudet”) i funktionsdefinitionen har dock försvunnit. Hur bör den se ut?

    import math
    A = math.pi*r**2
    return A*h

Alternativ 1: def volume_of_cylinder(r, h):

Alternativ 2: def volume_of_cylinder(A, h):

Alternativ 3: def volume_of_cylinder(A, r):

Alternativ 4: def dot_product(r, h):

Alternativ 5: def dot_product(A, h):

Alternativ 6: def dot_product(A, r):

Svar

Alternativ 1.

A2#

Nedan finns en klass Circle definierad.

import math

class Circle:

    def __init__(self, center=(0, 0), r=1):
        self.center = center
        self.r = r

    def __str__(self):
        return f'Circle centered at {self.center} with radius {self.r}.'
    
    def area(self):
        A = math.pi*self.r**2
        return A
    
    def move_to(self, new_center):
        self.center = new_center

Endast ett av följande påståenden är korrekt. Vilket?

Alternativ 1: Objekt av klassen Circle har tre instansvariabler: center, r och A.

Alternativ 2: Objekt av klassen Circle har alltid radie 1.

Alternativ 3: Metoden area tilldelar en av instansvariablerna ett nytt värde.

Alternativ 4: Metoden move_to tilldelar en av instansvariablerna ett nytt värde.

Svar

Alternativ 4.

A3#

Nedanstående program simulerar att ett antal tärningar kastas. Om deras värden uppfyller något visst villkor skrivs True ut.

import random

v1 = random.randint(1, 6)
v2 = v1
v3 = random.randint(1, 6)

if v1 + v2 < v3:
    print('True')

Vilket av nedanstående alternativ beskriver programmet bäst?

Alternativ 1: Tre olika tärningar kastas. Om summan av de två första blir större än den tredje skrivs True ut.

Alternativ 2: Tre olika tärningar kastas. Om summan av de två första blir mindre än den tredje skrivs True ut.

Alternativ 3: Två olika tärningar kastas. Om den andra blir mer än dubbelt så stor som den första skrivs True ut.

Alternativ 4: Två olika tärningar kastas. Om den andra blir mindre än den första skrivs True ut.

Svar

Alternativ 3.

A4#

Hur många listobjekt skapar nedanstående kod?

a1 = [i for i in range(0, 10)]
a2 = a1.copy()
a3 = [x for x in a1]
a4 = a1

Alternativ 1: Ett listobjekt. a2, a3 och a4 är referenser till existerande objekt.

Alternativ 2: Två listobjekt. a3 och a4 är referenser till existerande objekt.

Alternativ 3: Tre listobjekt. a4 är en referens till ett existerande objekt.

Alternativ 4: Fyra listobjekt. a1, a2, a3 och a4 refererar alla till olika listobjekt.

Svar

Alternativ 3.

A5: Prime numbers#

Skriv en funktion list_of_prime_numbers(limit) som returnerar en lista med alla primtal som är mindre än eller lika med heltalet limit. Du får kopiera in och anropa funktionen is_prime, som ges nedan. Denna funktion tar ett heltal större än eller lika med 2 som parameter och returnerar True om talet är ett primtal, annars False.

def is_prime(n):
    limit = int(n**0.5)
    for i in range(2, limit+1):
        if n % i == 0:
            return False
    return True

Följande kod:

print(list_of_prime_numbers(4))
print(list_of_prime_numbers(11))

ska ge utskriften

[2, 3]
[2, 3, 5, 7, 11]
Lösningsförslag
def list_of_prime_numbers(limit):
    lst = []
    for i in range(2, limit+1):
        if is_prime(i):
            lst.append(i)
    return lst

A6: Robber language#

När ett ord översätts till det så kallade rövarspråket ersätts alla konsonanter med ”xox”, där x är den ursprungliga konsonanten. Exempel:

  • b blir till bob

  • c blir till coc

osv.

Alla vokaler lämnas orörda.

Din uppgift är att skriva en funktion robber_language(word) som tar en sträng som representerar ett engelskt ord som parameter. Funktionen ska returnera ordet skrivet på rövarspråket. Du kan förutsätta att strängen word endast innehåller bokstäver och att alla bokstäver är gemener (små bokstäver).

Tips: Här är en sträng med de engelska konsonanterna: consonants = 'bcdfghjklmnpqrstvxz'

Följande kod:

print(robber_language('hello'))
print(robber_language('uppsala'))

ska ge utskriften

hohelollolo
upoppopsosalola
Lösningsförslag

Variant 1:

def robber_language(word):
    consonants = 'bcdfghjklmnpqrstvxz'
    new_word = ''
    for char in word:
        if char in consonants:
            new_word += char + 'o' + char
        else:
            new_word += char
    return new_word

Variant 2:

def robber_language(word):
    consonants = 'bcdfghjklmnpqrstvxz'
    for c in consonants:
        word = word.replace(c, c + 'o' + c)
    return word

A7: Lists of strings#

Skriv en funktion add_lists_of_strings(lst1, lst2) som tar två listor som parametrar. Listorna förutsätts vara lika långa och innehålla sträng-representationer av tal. Din funktion ska addera listorna elementvis och returnera resultatet. Den lista du returnerar ska också innehålla strängar!

Följande kod:

x = ['1', '2.4', '3.0']
y = ['0.9', '0.1', '0.5']
print(add_lists_of_strings(x, y))

ska ge utskriften

['1.9', '2.5', '3.5']
Lösningsförslag
def add_lists_of_strings(lst1, lst2):
    return [str(float(lst1[i]) + float(lst2[i])) for i in range(len(lst1))]

A8: Cheap meals#

I kassasystemet på en restaurang finns menyn sparad som ett lexikon där rätternas namn (strängar) är nycklar och priserna (heltal) är värden. Skriv en funktion list_of_cheap_meals(menu) som tar ett sådant lexikon som parameter. Funktionen ska returnera en lista med namnen på de rätter som kostar mindre än 100 kr.

Följande kod:

menu = {'burger': 89,
'pizza': 129,
'salad': 99,
'fish': 119}

print(list_of_cheap_meals(menu))

ska ge utskriften

['burger', 'salad']
Lösningsförslag
def list_of_cheap_meals(menu):
    cheap_meals = []
    for meal, price in menu.items():
        if price < 100:
            cheap_meals.append(meal)
    return cheap_meals

A9: Truncated Maclaurin#

Givet ett tal \(x\) kan talet \(e^x\) approximeras med de \(m\) första termerna i Maclaurin-utvecklingen. Låt approximationen betecknas \(S_m(x)\). Denna ges av:

\[ e^x \approx S_m(x) = \sum_{n=0}^{m-1} \frac{x^n}{n!} = 1 + x + \frac{x^2}{2!} + \frac{x^3}{3!} + \cdots + \frac{x^{m-1}}{(m-1)!} \]

Skriv en funktion truncated_maclaurin(x, m) som beräknar och returnerar \(S_m(x)\). Du kan förutsätta att m är ett positivt heltal (\(m\geq 1\)).

Tips: Använd math.factorial(n) för att beräkna \(n!\).

Följande kod:

print(round(truncated_maclaurin(1.0, 1), 4))
print(round(truncated_maclaurin(1.0, 3), 4))
print(round(truncated_maclaurin(1.0, 5), 4))
print(round(truncated_maclaurin(0.5, 4), 4))

ska ge utskriften

1.0
2.5
2.7083
1.6458
Lösningsförslag
import math

def truncated_maclaurin(x, m):
    result = 0
    for i in range(m):
        result += x**i/math.factorial(i)
    return result

A10: Broken code#

I koden nedan finns en funktion n_most_common_words(text, n). Funktionen tar en sträng (text) och ett positivt heltal (n) som parametrar. Funktionen ska returnera en lista med de n vanligaste orden i strängen text. Tyvärr har det smugit sig in ett antal mindre fel i koden.

Korrigera funktionen n_most_common_words så att den fungerar som avsett. Det går att få funktionen att fungera genom att korrigera tre av de existerande raderna, men du får göra så många ändringar du vill så länge slutresultatet fungerar korrekt.

OBS! Det kan finnas fel som inte ger några felmeddelanden men som gör att funktionen inte fungerar som avsett. Du måste även korrigera sådana fel.

import re

 
def part2(element):
    return element[1]

 
def n_most_common_words(text, n=1):
    list_of_words = re.findall(r'[a-zA-ZåäöÅÄÖ]+', text)
 
    # Create dict of words and number of occurrences
    word_to_count = {}
    for word in list_of_words
        word = word.lower()
        if word in word_to_count:
            word_to_count[word] += 1
        else:
            word_to_count['word'] = 1
 
    # Extract word-count pairs from dict
    words_and_counts = list(list_of_words.items())
    words_and_counts.sort(key=part2, reverse=True)
 
    # Return a list of the n most common words
    return [word for word, count in words_and_counts[:n]]
Lösningsförslag
def n_most_common_words(text, n=1):
    list_of_words = re.findall(r'[a-zA-ZåäöÅÄÖ]+', text)
 
    # Create dict of words and number of occurrences
    word_to_count = {}
    for word in list_of_words: # Saknades :
        word = word.lower()
        if word in word_to_count:
            word_to_count[word] += 1
        else:
            word_to_count[word] = 1 # Apostroferna skulle bort
 
    # Extract word-count pairs from dict
    words_and_counts = list(word_to_count.items()) #  list_of_words.items() skulle vara word_to_count.items()
    words_and_counts.sort(key=part2, reverse=True)
 
    # Return a list of the n most common words
    return [word for word, count in words_and_counts[:n]]

B1-B3: Matriser#

Nedan finns en klass Matrix definierad. Den innehåller en konstruktor och en __str__-metod. Dessa metoder är färdiga och du kommer inte behöva ändra dem. Kopiera koden till en editor och läs sedan instruktionerna som kommer efter koden.

import random

class Matrix:

    def __init__(self, num_rows, num_cols, values=None):
        self.num_rows = num_rows
        self.num_cols = num_cols
        if values is None:
            # Generate random values
            self.values = []
            for _ in range(num_rows):
                row = [random.randint(-10, 10) for _ in range(num_cols)]
                self.values.append(row)
        else:
            # Take user-supplied values
            self.values = values

    def __str__(self):
        output = ''
        for i in range(self.num_rows):
            for j in range(self.num_cols):
                value = self.values[i][j]
                output += f'{value:6.1f}'
            output += '\n'
        return output

Objekt av klassen Matrix representerar matematiska matriser. En matris har ett antal rader och kolonner och är fylld med tal. Här är ett exempel på en matris \(A\) med 2 rader och 3 kolonner:

\[\begin{split} A = \begin{bmatrix} -4 & -3.2 & 1 \\ 9 & 18.1 & -5.6 \end{bmatrix} \end{split}\]

Följande är viktig information om klassen Matrix:

  1. Konstruktorn och __str__-metoden är färdig och du ska inte ändra dem. Du behöver läsa konstruktorn och förstå hur den fungerar. Du uppmuntras också att använda __str__-metoden för att kontrollera att du löst uppgifterna korrekt.

  2. Klassen Matrix har tre instansvariabler. Instansvariabeln values är en lista av listor. Var och en av de inre listorna innehåller tal och svarar mot en rad i matrisen.

  3. När ett Matrix-objekt skapas kan vi ange hur många rader och kolonner matrisen ska ha, samt vilka värden den ska innehålla. Om värdena utelämnas fylls matrisen med slumpade heltal mellan -10 och 10.

Uppgift B1 (3p)#

Lägg till en metod max i klassen. Metoden ska returnera det största talet i matrisen. Notera att det inte ska vara det största absolutbeloppet. Här räknas alltså 2 som större än -9, till exempel.

Lösningsförslag
    def max(self):
        return max([max(row) for row in self.values])

Uppgift B2 (4p)#

Skriv en funktion matrix_add(A, B). Funktionen ska ta två objekt av klassen Matrix som parametrar och returnera ett nytt Matrix-objekt som motsvarar summan av A och B. Kom ihåg att matriser adderas elementvis. Exempel:

\[\begin{split} \begin{bmatrix} 1 & 2 & 3 \\ 0 & 1 & 4 \end{bmatrix} + \begin{bmatrix} 0 & 1 & 2 \\ 1 & 2 & -1 \end{bmatrix} = \begin{bmatrix} 1 & 3 & 5 \\ 1 & 3 & 3 \end{bmatrix} \end{split}\]

Notera att matrix_add ska vara en funktion, inte en metod i klassen Matrix. Du kan förutsätta att A har samma antal rader och samma antal kolonner som B (annars är matrisadditionen inte matematiskt definierad).

Lösningsförslag
def matrix_add(A, B):
    # Assume that A and B have the same dimensions
    num_rows = A.num_rows
    num_cols = A.num_cols

    # Create new values of size (num_cols) x (num_rows),
    # temporarily filled with None
    values = [[None]*num_cols for _ in range(num_rows)]
    for i in range(num_rows):
        for j in range(num_cols):
            values[i][j] = A.values[i][j] + B.values[i][j]

    return Matrix(num_rows, num_cols, values)

Uppgift B3 (4p)#

Lägg till en metod transpose i klassen Matrix. Metoden ska förändra Matrix-objektet så att det istället motsvarar transponatet av den ursprungliga matrisen. Metoden ska alltså inte ha något returvärde.

Kom ihåg att transponatet av en matris innebär att rader och kolonner byter plats. Exempelvis, om

\[\begin{split} A = \begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{bmatrix} \end{split}\]

så ges transponatet av \(A\) av

\[\begin{split} A^T = \begin{bmatrix} 1 & 4 \\ 2 & 5 \\ 3 & 6 \end{bmatrix}. \end{split}\]
Lösningsförslag
    def transpose(self):
        # Create new values of size (num_cols) x (num_rows),
        # temporarily filled with None
        new_values = [[None]*self.num_rows for _ in range(self.num_cols)]

        # Fill new_values with transposed values from self.values
        for i in range(self.num_rows):
            for j in range(self.num_cols):
                new_values[j][i] = self.values[i][j]
        
        # Replace attributes
        self.values = new_values
        num_cols = self.num_cols
        self.num_cols = self.num_rows
        self.num_rows = num_cols