Lösningsförslag 2022-08-17#

Del A#

Lösning A1

Rätt alternativ: D

Kommentarer: Den officiella dokumentationen säger: ”A module is a file containing Python definitions and statements.” En modul kan alltså innehålla script-kod. Ett paket (package) är en samling av paket och moduler, dvs. inte samma sak som en modul.

Kursen går inte igenom .pym-filer, så ni förväntas inte veta vad det är.

Lösning A2

Rätt alternativ: C

Kommentar: Operatorn + innebär konkatenering (=ihopslagning) för strängar.

Lösning A3

Rätt alternativ: B

Kommentarer: Variabeln pinch_of_salt definieras i funktionen add_pinch_of_salt som en lokal variabel. Denna är helt orelaterad till variabeln pinch_of_salt i det globala scopet.

Lexikonet cake används i funktionen utan att ha definierats lokalt. Därför används cake från det globala scopet. Förändringen av cake sker alltså även i det globala scopet.

Motiveringen är tyvärr inte helt korrekt. Att den globala variabeln pinch_of_salt är oförändrad efter funktionsanropet beror inte på att floats är oföränderliga (vilket visserligen är sant) utan på att pinch_of_salt i funktionen är en lokal variabel som inte påverkar den globala variabeln med samma namn.

Lösning A4

Rätt alternativ: C

Lösning A5

Rätt alternativ: D

Lösning A6

Rätt alternativ: C

Lösning A7

Rätt alternativ: B

Lösning A8

Rätt alternativ: D

Lösning A9

Rätt alternativ: B

Lösning A10

Rätt alternativ: C

Del B#

B1#

Lösningsförslag
class Pizza:
    def __init__(self, flour, topping, crust):
        self.flour = flour
        self.topping = topping
        self.crust = crust

B2#

Lösningsförslag
class Pizza:
    def __init__(self, flour, topping, crust):
        self.flour = flour
        self.topping = topping
        self.crust = crust

        available_flour = ['white', 'brown', 'wholegrain']
        available_toppings = ['mozzarella', 'cheddar', 'gorgonzola']
        available_crusts = ['thin', 'sicilian', 'focaccia']

        if flour in available_flour and topping in available_toppings and crust in available_crusts:
            self.startBaking() # Only works if the method startBaking is implemented
        else:
            print('Pizza aborted, one of the input ingredients is out of stock.')

B3#

Lösningsförslag
import time

class Pizza:
    def __init__(self, flour, topping, crust):
        self.flour = flour
        self.topping = topping
        self.crust = crust

        available_flour = ['white', 'brown', 'wholegrain']
        available_toppings = ['mozzarella', 'cheddar', 'gorgonzola']
        available_crusts = ['thin', 'sicilian', 'focaccia']

        if flour in available_flour and topping in available_toppings and crust in available_crusts:
            self.startBaking()
        else:
            print('Pizza aborted, one of the input ingredients is out of stock.')

    def startBaking(self):
        self.ready = False # True/False instead of suggested 1 or 0.
        self.time_start = time.time()

B4#

Lösningsförslag
    def isReady(self):
        time_limit = 5*60 # 5 minutes converted to seconds
        time_passed = time.time() - self.time_start
        if time_passed > time_limit:
            self.ready = True
            print(f'Pizza with {self.topping} topping, {self.flour} flour dough and {self.crust} crust is ready!')
        else:
            time_remaining = (time_limit - time_passed)/60
            print(f'Pizza with {self.topping} topping, {self.flour} flour dough and {self.crust} crust is still in the oven!')
            print(f'Wait for another {time_remaining:.2f} minutes.')

B5 (class Pizzeria)#

Lösningsförslag
import random

class Pizzeria:
    def __init__(self, N):
        available_flour = ['white', 'brown', 'wholegrain']
        available_toppings = ['mozzarella', 'cheddar', 'gorgonzola']
        available_crusts = ['thin', 'sicilian', 'focaccia']

        # Create N pizzas with random flour, topping, and crust
        self.allPizzas = []
        for _ in range(N):
            flour = available_flour[random.randint(0, len(available_flour)-1)]
            topping = available_toppings[random.randint(0, len(available_toppings)-1)]
            crust = available_crusts[random.randint(
                0, len(available_crusts)-1)]

            self.allPizzas.append(Pizza(flour, topping, crust))

    def priceOfAllPizzas(self):
        number_of_pizzas = len(self.allPizzas)
        flour_prices = {'white':10, 'brown':15, 'wholegrain':20}
        topping_prices = {'mozzarella': 3, 'cheddar': 6, 'gorgonzola': 9}
        crust_prices = {'thin': 2, 'sicilian': 4, 'focaccia': 7}

        cost = 0
        all_ingredients_available = True
        for pizza in self.allPizzas:
            if pizza.topping not in topping_prices:
                all_ingredients_available = False
            if pizza.flour not in flour_prices:
                all_ingredients_available = False
            if pizza.crust not in crust_prices:
                all_ingredients_available = False

            cost += topping_prices[pizza.topping]
            cost += flour_prices[pizza.flour]
            cost += crust_prices[pizza.crust]

        if all_ingredients_available:
            print(f'The total cost for {number_of_pizzas} pizzas is {cost} units.')
        else:
            print('One or more ingredients unavailable. Order aborted')

B6 (extra topping)#

Frågan är inte helt tydlig. Här tolkar vi det som att den extra toppingen läggs till som en ytterligare instansvariabel, här med namn extra_topping.

Lösningsförslag

För att få med den extra toppingen i utskriften av isReady() ändrar vi i konstruktorn och isReady() när metoden addExtraTopping() läggs till.

    def __init__(self, flour, topping, crust):
        self.flour = flour
        self.topping = topping
        self.crust = crust
        self.extra_topping = ''

        available_flour = ['white', 'brown', 'wholegrain']
        available_toppings = ['mozzarella', 'cheddar', 'gorgonzola']
        available_crusts = ['thin', 'sicilian', 'focaccia']

        if flour in available_flour and topping in available_toppings and crust in available_crusts:
            self.startBaking()
        else:
            print('Pizza aborted, one of the input ingredients is out of stock.')

    def isReady(self):
        time_limit = 5*60 # 5 minutes converted to seconds
        time_passed = time.time() - self.time_start

        if self.extra_topping == '':
            extra_topping_str = ''
        else:
            extra_topping_str = f'extra {self.extra_topping}, '

        if time_passed > time_limit:
            self.ready = True  # True/False instead of suggested 1 or 0.
            print(f'Pizza with {extra_topping_str}{self.topping} topping, {self.flour} flour dough and {self.crust} crust is ready!')
        else:
            time_remaining = (time_limit - time_passed)/60
            print(f'Pizza with {extra_topping_str}{self.topping} topping, {self.flour} flour dough and {self.crust} crust is still in the oven!')
            print(f'Wait for another {time_remaining:.2f} minutes.')

    def addExtraTopping(self, topping):
        allowed = ['prosciutto', 'funghi', 'pineapple']
        if not self.ready:
            if topping in allowed:
                self.extra_topping = topping
            else:
                print('That topping is not available!')
        else:
            print("Can't add topping, pizza is already done!")

B1-B6#

Lösningsförslag med enkel testkod.
import time
import random

class Pizza:
    def __init__(self, flour, topping, crust):
        self.flour = flour
        self.topping = topping
        self.crust = crust
        self.extra_topping = ''

        available_flour = ['white', 'brown', 'wholegrain']
        available_toppings = ['mozzarella', 'cheddar', 'gorgonzola']
        available_crusts = ['thin', 'sicilian', 'focaccia']

        if flour in available_flour and topping in available_toppings and crust in available_crusts:
            self.startBaking()
        else:
            print('Pizza aborted, one of the input ingredients is out of stock.')

    def startBaking(self):
        self.ready = False # True/False instead of suggested 1 or 0.
        self.time_start = time.time()

    def isReady(self):
        time_limit = 5*60 # 5 minutes converted to seconds
        time_passed = time.time() - self.time_start

        if self.extra_topping == '':
            extra_topping_str = ''
        else:
            extra_topping_str = f'extra {self.extra_topping}, '

        if time_passed > time_limit:
            self.ready = True  # True/False instead of suggested 1 or 0.
            print(f'Pizza with {extra_topping_str}{self.topping} topping, {self.flour} flour dough and {self.crust} crust is ready!')
        else:
            time_remaining = (time_limit - time_passed)/60
            print(f'Pizza with {extra_topping_str}{self.topping} topping, {self.flour} flour dough and {self.crust} crust is still in the oven!')
            print(f'Wait for another {time_remaining:.2f} minutes.')

    def addExtraTopping(self, topping):
        allowed = ['prosciutto', 'funghi', 'pineapple']
        if not self.ready:
            if topping in allowed:
                self.extra_topping = topping
            else:
                print('That topping is not available!')
        else:
            print("Can't add topping, pizza is already done!")

class Pizzeria:
    def __init__(self, N):
        available_flour = ['white', 'brown', 'wholegrain']
        available_toppings = ['mozzarella', 'cheddar', 'gorgonzola']
        available_crusts = ['thin', 'sicilian', 'focaccia']

        self.allPizzas = []
        for _ in range(N):
            flour = available_flour[random.randint(0, len(available_flour)-1)]
            topping = available_toppings[random.randint(0, len(available_toppings)-1)]
            crust = available_crusts[random.randint(
                0, len(available_crusts)-1)]

            self.allPizzas.append(Pizza(flour, topping, crust))

    def priceOfAllPizzas(self):
        number_of_pizzas = len(self.allPizzas)
        flour_prices = {'white':10, 'brown':15, 'wholegrain':20}
        topping_prices = {'mozzarella': 3, 'cheddar': 6, 'gorgonzola': 9}
        crust_prices = {'thin': 2, 'sicilian': 4, 'focaccia': 7}

        cost = 0
        all_ingredients_available = True
        for pizza in self.allPizzas:
            if pizza.topping not in topping_prices:
                all_ingredients_available = False
            if pizza.flour not in flour_prices:
                all_ingredients_available = False
            if pizza.crust not in crust_prices:
                all_ingredients_available = False

            cost += topping_prices[pizza.topping]
            cost += flour_prices[pizza.flour]
            cost += crust_prices[pizza.crust]

        if all_ingredients_available:
            print(f'The total cost for {number_of_pizzas} pizzas is {cost} units.')
        else:
            print('One or more ingredients unavailable. Order aborted')

myDeliciousPizza = Pizza('wholegrain', 'cheddar', 'sicilian')
time.sleep(3)
myDeliciousPizza.isReady()
myDeliciousPizza.addExtraTopping('prosciutto')
myDeliciousPizza.isReady()

my_pizzeria = Pizzeria(5)
my_pizzeria.priceOfAllPizzas()

B7 (Equation parsing)#

Lösningsförslag
def is_valid_equation(equation):
    numbers = [str(i) for i in range(0, 10)]
    admissible_chars = numbers + ['=', '+']

    # Check that only admissible characters are in equation
    for char in equation:
        if char not in admissible_chars:
            return False

    # Check that there is exactly one =
    if equation.count('=') != 1:
        return False

    # Check for numbers left and right of =
    for part in equation.split('='):
        for number in part.split('+'):
            # Ensure that no element in part.split('+') is empty.
            # An empty element implies one of the following:
            # 1. part starts or ends with '+'
            # 2. part contains '++' (two pluses in a row)
            if len(number) == 0:
                return False

    # All checks passed
    return True
Kod som testar funktionen med ekvationerna i uppgiften
valid_equations = [
    '1+2=456',
    '12+34=40+3+2+01',
    '123=143',
    '12+3=1+23',
    '123=103+20',
    '0+00+000+00100=00000+000000'
]

invalid_equations = [
    '1+2=3=2+1',
    '12=3*4',
    'a+1=1+a',
    '12++3=15',
    'tralala',
    '1 + 2 = 3',
    'c#',
    '1+2+=3',
    '='
]

print('--- Valid equations ---')
for eq in valid_equations:
    print(f'{eq}: {is_valid_equation(eq)}')

print('--- Invalid equations ---')
for eq in invalid_equations:
    print(f'{eq}: {is_valid_equation(eq)}')

B8 (A river of spaces)#

Den här uppgiften är i svåraste laget, även för del B.

Lösningsförslag
def longest_river_of_spaces(list_of_lines):
    # The dict "counts" stores the length of the longest river
    # that leads to a certain space character in the text, from above.
    # As keys in count, we use tuples of the form: (line_index, character_index)
    counts = {}

    for line_ind, line in enumerate(list_of_lines):
        for char_ind, char in enumerate(line):
            if char == ' ':
                # We found a space! Might be the start of a river.
                counts[(line_ind, char_ind)] = 1

                # Check if this space is actually part of an existing river.
                for shift in range(-1, 2):
                    if (line_ind-1, char_ind + shift) in counts:
                        # If this space is part of multiple existing rivers,
                        # then pick the longest.
                        counts[(line_ind, char_ind)] = max(
                            counts[(line_ind, char_ind)], 1 + counts[(line_ind-1, char_ind + shift)])
    # Return the length of the longest river (or 0, if no spaces were found)
    if counts == {}:
        return 0
    return max(counts.values())
Kod som testar funktionen med texten i uppgiften
text_block = [
    'Two fierce and enormous bears, distinguished',
    'by the appellations of Innocence and Mica',
    'Aurea, could alone deserve to share the',
    'favour of Maximin. The cages of those trusty',
    'guards were always placed near the',
    'bed-chamber of Valentinian, who frequently',
    'amused his eyes with the grateful spectacle',
    'of seeing them tear and devour the bleeding',
    'limbs of the malefactors who were abandoned',
    'to their rage. Their diet and exercises were',
    'carefully inspected by the Roman emperor;',
    'and, when Innocence had earned her discharge',
    'by a long course of meritorious service, the',
    'faithful animal was again restored to',
    'the freedom of her native woods.'
]
print(longest_river_of_spaces(text_block))