List comprehension

Deze pagina bevat uitvoerbare code.

Gebruik list comprehension om de volgende problemen op te lossen.

  1. Begin met een schone kernel.

  2. Vul per opdracht de startcel in en voer die uit.

  3. Voer daarna de testcel eronder uit. Bij oningevulde code kunnen assertions falen; als alle tests slagen, verschijnt er geen uitvoer.

  4. Start na het bekijken van de oplossingen opnieuw met een schone kernel voordat je je eigen code test.

Opgaven

Opdracht 1: mult_of_five

De functie mult_of_five accepteert een integer n en heeft als resultaat een lijst met de veelvouden 1 tot en met n van 5. Gebruik hier de ingebouwde functie range.

De functie range begint met 0, je zult voor 1 als volgt moeten corrigeren:

range(1, n + 1)

Zie verder ook de documentatie voor range.

def mult_of_five(n):
    """Geeft de eerste n veelvouden van 5."""
    return [...]
assert mult_of_five(0) == []
assert mult_of_five(1) == [5]
assert mult_of_five(2) == [5, 10]
assert mult_of_five(3) == [5, 10, 15]

Opdracht 2: divisible_by

De functie divisible_by accepteert een integer n en een lijst L en geeft als resultaat een lijst terug met alle waarden in L deelbaar door n.

def divisible_by(n, L):
    """Geeft de waarden in L die deelbaar zijn door n."""
    return [...]
assert divisible_by(5, [15, 0, 23, 4]) == [15, 0]
assert divisible_by(3, [2, 4, 8, 10]) == []
assert divisible_by(2, []) == []

Opdracht 3: starts_with

De functie starts_with accepteert een string s en een lijst met strings L en heeft als resultaat een lijst met alle waarden in L die beginnen met s.

  • je mag de ingebouwde string methode startswith niet gebruiken

def starts_with(s, L):
    """Geeft de strings in L die beginnen met s."""
    return [...]
assert starts_with("a", []) == []
assert starts_with("a", ["bbc", "brits", "omroep"]) == []
assert starts_with("a", ["abc", "cde", "aha", "abba"]) == ["abc", "aha", "abba"]
assert starts_with("ab", ["abc", "cde", "aha", "abba"]) == ["abc", "abba"]
assert starts_with("abc", ["abc", "cde", "aha", "abba"]) == ["abc"]
assert starts_with("abcd", ["abc", "cde", "aha", "abba"]) == []

Opdracht 4: double_letters

De functie double_letters accepteert een lijst met strings L en heeft als resultaat een lijst met alle waarden verdubbeld.

def double_letters(L):
    """Geeft elke string in L tweemaal achter elkaar."""
    return [...]
assert double_letters(["a"]) == ["aa"]
assert double_letters(["a", "1", "23"]) == ["aa", "11", "2323"]
assert double_letters([]) == []

Opdracht 5: num_as

De functie num_as accepteert een lijst met strings L en geeft als integer het aantal keer dat de string “a” in L voorkomt.

def num_as(L):
    """Telt hoe vaak de string "a" in L voorkomt."""
    return sum([...])
assert num_as(["a", "b", "c", "a", "d"]) == 2
assert num_as(["y", "b", "c", "x", "d"]) == 0
assert num_as([]) == 0

Opdracht 6: vwl

De functie vwl accepteert een string s en heeft als resultaat het aantal klinkers in s. Klinkers zijn “a”, “e”, “i”, “o” en “u”.

  • gebruik hier de ingebouwde functie sum in combinatie met list comprehension

def vwl(s):
    """Geeft het aantal klinkers in s."""
    ...
assert vwl("appel") == 2
assert vwl("bbc") == 0
assert vwl("oma") == 2
assert vwl("") == 0

Opdracht 7: add_tax

De functie add_tax accepteert een getal (float of integer) t groter dan 0 en een lijst met getallen L. t staat voor een percentage, L bevat prijzen van artikelen en het resultaat is een lijst met prijzen verhoogd met percentage t.

def add_tax(t, L):
    """Verhoogt elke prijs in L met t procent."""
    return [...]
assert add_tax(25, [10, 100, 30, 40]) == [12.5, 125.0, 37.5, 50.0]
assert add_tax(25, [0, 16, 8]) == [0.0, 20.0, 10.0]

Opdracht 8: above_below_freeze

De functie above_below_freeze accepteert een lijst met getallen (integer of float) L die staan voor temperaturen en heeft als resultaat een lijst waar voor elke waarde “onder”, “boven” of “gelijk” aan het vriespunt (0 graden) is ingevuld.

  • schrijf hier een hulpfunctie voor het bepalen van “onder”, “boven” of “gelijk” voor een temperatuur en gebruik deze functie in de list comprehension

def above_below_freeze(L):
    """Geeft per temperatuur in L de ligging ten opzichte van het vriespunt."""
    ...
assert above_below_freeze([-1, 0, 10]) == ["onder", "gelijk", "boven"]
assert above_below_freeze([21, -21, 0]) == ["boven", "onder", "gelijk"]

Opdracht 9: zipper

De functie zipper accepteert twee lijsten L1 en L2 van gelijke lengte. Het resultaat is een lijst van lijsten waar elk element de paarsgewijze combinatie (“ritsen”) van waarden in L1 en L2 is.

def zipper(L1, L2):
    """Combineert L1 en L2 paarsgewijs tot een lijst van lijsten."""
    return [...]
assert zipper([1, 3, 5], [2, 4, 6]) == [[1, 2], [3, 4], [5, 6]]
assert zipper([10, 9, 12], ["jan", "feb", "mrt"]) == [
    [10, "jan"],
    [9, "feb"],
    [12, "mrt"],
]

Opdracht 10: only_leaps

Een jaar is een schrikkeljaar als het deelbaar is door 4, behalve als het ook deelbaar is door 100 - tenzij het dan ook deelbaar is door 400. Zo is 2024 een schrikkeljaar, 1900 niet en 2000 wel.

De functie only_leaps accepteert een lijst met integers L die staan voor jaartallen en heeft als resultaat een lijst met de schrikkeljaren in L.

  • gebruik de hulpfunctie is_leap in de list comprehension.

def is_leap(y):
    """Geeft True als y een schrikkeljaar is, en anders False."""
    return y % 4 == 0 and (y % 100 != 0 or y % 400 == 0)


def only_leaps(L):
    """Geeft de schrikkeljaren in L."""
    return [...]
assert only_leaps([]) == []
assert only_leaps([1900, 2000, 2024]) == [2000, 2024]
assert only_leaps([2021, 2022, 2023]) == []

Opdracht 11: mul_table

Een vermenigvuldigingstabel ziet er als volgt uit

vermenigvuldigingstabel

Met een vermenigvuldigingstabel kan het resultaat van een vermenigvuldiging worden opgezocht, bijvoorbeeld om het resultaat van \(4 \times 4\) op te zoeken kijk je waar de kolommen en rijen elkaar kruisen op positie 4 (dit is 16).

De functie mul_table accepteert een integer n en heeft als resultaat de vermenigvuldigingstabel van n als een lijst van lijsten. Gebruik hier de ingebouwde functie range.

Bijvoorbeeld, mul_table(5) zal het volgende resultaat geven:

[
    [1, 2, 3, 4, 5],
    [2, 4, 6, 8, 10],
    [3, 6, 9, 12, 15],
    [4, 8, 12, 16, 20],
    [5, 10, 15, 20, 25],
]

De functie range begint met 0, je zult voor 1 als volgt moeten corrigeren:

range(1, n + 1)

Zie verder ook de documentatie voor range.

def mul_table(n):
    """Geeft de vermenigvuldigingstabel van n."""
    return [...]
assert mul_table(0) == []
assert mul_table(1) == [[1]]
assert mul_table(2) == [[1, 2], [2, 4]]
assert mul_table(3) == [[1, 2, 3], [2, 4, 6], [3, 6, 9]]

Opdracht 12: running_totals

Een tussenstand is de som van alles wat tot en met een bepaalde positie is geteld. Bij de lijst [1, 2, 3] is de tussenstand na het eerste getal 1, na het tweede 3 en na het derde 6.

De functie running_totals accepteert een lijst met getallen L en heeft als resultaat een lijst die op elke positie de som van L tot en met die positie geeft. Het resultaat is dus even lang als L.

  • gebruik de ingebouwde functie sum in combinatie met list comprehension

  • gebruik de ingebouwde functie range en een slice van L

def running_totals(L):
    """Geeft de tussenstand van L op elke positie."""
    return [...]
assert running_totals([]) == []
assert running_totals([10]) == [10]
assert running_totals([1, 2, 3]) == [1, 3, 6]
assert running_totals([5, -5, 5]) == [5, 0, 5]

Uitwerkingen

Opdracht 1: mult_of_five

def mult_of_five(n):
    """Geeft de eerste n veelvouden van 5."""
    return [x * 5 for x in range(1, n + 1)]
assert mult_of_five(0) == []
assert mult_of_five(1) == [5]
assert mult_of_five(2) == [5, 10]
assert mult_of_five(3) == [5, 10, 15]

Opdracht 2: divisible_by

def divisible_by(n, L):
    """Geeft de waarden in L die deelbaar zijn door n."""
    return [x for x in L if x % n == 0]
assert divisible_by(5, [15, 0, 23, 4]) == [15, 0]
assert divisible_by(3, [2, 4, 8, 10]) == []
assert divisible_by(2, []) == []

Opdracht 3: starts_with

def starts_with(s, L):
    """Geeft de strings in L die beginnen met s."""
    return [x for x in L if s == x[:len(s)]]
assert starts_with("a", []) == []
assert starts_with("a", ["bbc", "brits", "omroep"]) == []
assert starts_with("a", ["abc", "cde", "aha", "abba"]) == ["abc", "aha", "abba"]
assert starts_with("ab", ["abc", "cde", "aha", "abba"]) == ["abc", "abba"]
assert starts_with("abc", ["abc", "cde", "aha", "abba"]) == ["abc"]
assert starts_with("abcd", ["abc", "cde", "aha", "abba"]) == []

Opdracht 4: double_letters

def double_letters(L):
    """Geeft elke string in L tweemaal achter elkaar."""
    return [x * 2 for x in L]
assert double_letters(["a"]) == ["aa"]
assert double_letters(["a", "1", "23"]) == ["aa", "11", "2323"]
assert double_letters([]) == []

Opdracht 5: num_as

def num_as(L):
    """Telt hoe vaak de string "a" in L voorkomt."""
    return sum([1 for x in L if x == "a"])
assert num_as(["a", "b", "c", "a", "d"]) == 2
assert num_as(["y", "b", "c", "x", "d"]) == 0
assert num_as([]) == 0

Opdracht 6: vwl

def vwl(s):
    """Geeft het aantal klinkers in s."""
    return sum([1 for x in s if x in "aeiou"])
assert vwl("appel") == 2
assert vwl("bbc") == 0
assert vwl("oma") == 2
assert vwl("") == 0

Opdracht 7: add_tax

def add_tax(t, L):
    """Verhoogt elke prijs in L met t procent."""
    return [x + (x * t / 100) for x in L]
assert add_tax(25, [10, 100, 30, 40]) == [12.5, 125.0, 37.5, 50.0]
assert add_tax(25, [0, 16, 8]) == [0.0, 20.0, 10.0]

Opdracht 8: above_below_freeze

def above_below(t):
    """Geeft of t boven, onder of gelijk aan het vriespunt is."""
    if t > 0:
        return "boven"
    elif t < 0:
        return "onder"
    else:
        return "gelijk"

def above_below_freeze(L):
    """Geeft per temperatuur in L de ligging ten opzichte van het vriespunt."""
    return [above_below(x) for x in L]
assert above_below_freeze([-1, 0, 10]) == ["onder", "gelijk", "boven"]
assert above_below_freeze([21, -21, 0]) == ["boven", "onder", "gelijk"]

Opdracht 9: zipper

def zipper(L1, L2):
    """Combineert L1 en L2 paarsgewijs tot een lijst van lijsten."""
    return [[x, y] for x, y in zip(L1, L2)]
assert zipper([1, 3, 5], [2, 4, 6]) == [[1, 2], [3, 4], [5, 6]]
assert zipper([10, 9, 12], ["jan", "feb", "mrt"]) == [
    [10, "jan"],
    [9, "feb"],
    [12, "mrt"],
]

Opdracht 10: only_leaps

def is_leap(y):
    """Geeft True als y een schrikkeljaar is, en anders False."""
    return y % 4 == 0 and (y % 100 != 0 or y % 400 == 0)


def only_leaps(L):
    """Geeft de schrikkeljaren in L."""
    return [y for y in L if is_leap(y)]
assert only_leaps([]) == []
assert only_leaps([1900, 2000, 2024]) == [2000, 2024]
assert only_leaps([2021, 2022, 2023]) == []

Opdracht 11: mul_table

Stel dat we een \(N \times N\) vermenigvuldigingstabel willen opstellen voor \(N=5\)

n = 5

Met behulp van de tip (range laten tellen vanaf 1) kan een eerste rij van de tabel met een list comprehension worden opgesteld

[col for col in range(1, n + 1)]
[1, 2, 3, 4, 5]

Omdat dit de eerste vermenigvuldiging is van col (de eerste rij, dus 1) kan het ook worden geschreven als

[col * 1 for col in range(1, n + 1)]
[1, 2, 3, 4, 5]

Volgens dit patroon zal de tweede rij in de vermenigvuldigingstabel als volgt zijn

[col * 2 for col in range(1, n + 1)]
[2, 4, 6, 8, 10]

Meer algemeen kan dit worden geschreven als

[col * row for col in range(1, n + 1)]

waar col staat voor de kolom (5 posities in totaal, want \(N=5\)) en row voor de rij (ook 5 in totaal, want \(N=5\)). De rijen moeten n keer worden herhaald voor row zodat je uiteindelijk met list comprehension tot de volgende lijst van lijsten kan komen

def mul_table(n):
    """Geeft de vermenigvuldigingstabel van n."""
    return [[col * row for col in range(1, n + 1)] for row in range(1, n + 1)]
assert mul_table(0) == []
assert mul_table(1) == [[1]]
assert mul_table(2) == [[1, 2], [2, 4]]
assert mul_table(3) == [[1, 2, 3], [2, 4, 6], [3, 6, 9]]

Opdracht 12: running_totals

Ook hier moet een patroon worden gevonden voor de index-waarden, en daarmee het gebruik van range. Het helpt om dit eerst uit te schrijven, bijvoorbeeld voor de lijst [1, 2, 3] zoals die in de assertions wordt gebruikt.

De tussenstand op positie 0 is de som van het eerste getal, op positie 1 de som van de eerste twee en op positie 2 de som van alle drie. Dat zijn slices van L die telkens een element langer worden:

[sum(L[0:1]), sum(L[0:2]), sum(L[0:3])]

De bovengrens van de slice loopt dus van 1 tot en met de lengte van L, en dat is i + 1 als i met range van 0 tot de lengte loopt. De ondergrens is altijd 0 en mag worden weggelaten.

def running_totals(L):
    """Geeft de tussenstand van L op elke positie."""
    return [sum(L[: i + 1]) for i in range(len(L))]
assert running_totals([]) == []
assert running_totals([10]) == [10]
assert running_totals([1, 2, 3]) == [1, 3, 6]
assert running_totals([5, -5, 5]) == [5, 0, 5]