Basis¶
Wat een tekst over zichzelf zegt, korter¶
In de basisopgave van week 1 schreef je functies die een tekst doorlichten: hoeveel woorden, welk woord het vaakst, hoeveel woorden maar één keer. Dat waren allemaal lussen. Een deel ervan bouwt een nieuwe lijst, set of dictionary op, en dat past nu op één regel. Een ander deel blijft een lus.
In deze opgave schrijf je functies over dezelfde teksten. Bij elke functie beslis je welke vorm past, en je vertaalt in beide richtingen: van lus naar comprehension en van comprehension naar lus.
Lus of comprehension¶
Een lus die een nieuwe verzameling opbouwt, met per element hoogstens één ding erbij, schrijf je als comprehension. Het lusrecept wijst de delen aan:
result = [] # 1. verzamelen: een nieuwe lijst
for word in words: # 2. langs de woorden
if len(word) <= 3: # 3. of het erbij komt
result.append(word) # 3. wat erbij komt
wordt
result = [word for word in words if len(word) <= 3]
Een lus blijft een lus als hij per stap iets bijwerkt wat er al was, iets onthoudt tussen de stappen, of per stap iets doet in plaats van iets te verzamelen. Zie het tweede college van deze week.
De teksten¶
assets/teksten/kort.txt en assets/teksten/vuurtoren.txt zijn dezelfde
bestanden als in week 1. read_text en word_list uit week 1 staan in de cel
hieronder klaar. Voer die cel eerst uit.
TEKSTEN = "assets/teksten/"
LEESTEKENS = ".,!?;:"
def read_text(filename):
"""Leest een heel bestand en geeft de inhoud terug als één string."""
with open(filename) as file:
return file.read()
def word_list(text):
"""Geeft de woorden van text in kleine letters, zonder leestekens."""
schoon = ""
for teken in text.lower():
if teken in LEESTEKENS:
schoon = schoon + " "
else:
schoon = schoon + teken
return schoon.split()
Wat je gaat maken¶
Stap |
Functie |
Doet |
Wat je oefent |
|---|---|---|---|
1 |
|
de woorden die langer zijn dan |
een list comprehension schrijven |
2 |
|
tellen hoe vaak elk woord voorkomt |
kiezen tussen lus en comprehension |
3 |
|
tellen hoeveel woorden maar één keer voorkomen |
van lus naar comprehension |
4 |
|
een woord opdelen in stukjes van |
van comprehension naar lus |
5 |
|
de telling als lijst van lijsten |
een lijst van lijsten opbouwen |
6 |
|
het aandeel van de vaakst gebruikte woorden |
filteren in een lijst van lijsten |
Stap 1: long_words(words, n)¶
Geeft een lijst met de woorden uit words die langer zijn dan n letters, in
dezelfde volgorde. Gebruik een list comprehension.
Aanroep |
Resultaat |
|---|---|
|
|
|
|
|
|
# jouw oplossing
assert long_words(["de", "vuurtoren", "draait"], 5) == ["vuurtoren", "draait"]
assert long_words([], 5) == []
assert long_words(word_list(read_text(TEKSTEN + "vuurtoren.txt")), 11) == [
"zesenvijftig",
"tweehonderdzesendertig",
"vuurtorenwachter",
"geschiedenis",
"tegenwoordig",
]
Stap 2: count_words(words)¶
Geeft een dictionary die elk woord koppelt aan het aantal keren dat het voorkomt.
Aanroep |
Resultaat |
|---|---|
|
|
|
|
Er zijn twee manieren, en ze geven allebei hetzelfde resultaat:
Lus, uit week 1 |
Dict comprehension |
|---|---|
begin met |
|
Kies één van de twee, schrijf count_words en zet er in een commentaarregel bij
waarom je die kiest. Denk daarbij aan wat er per stap gebeurt: hoe vaak loopt
elke versie de woorden langs? Kijk je keuze na met de tekst onder de testcel.
# jouw oplossing
assert count_words(["spam", "spam", "taart"]) == {"spam": 2, "taart": 1}
assert count_words([]) == {}
assert count_words(word_list(read_text(TEKSTEN + "kort.txt")))["taarten"] == 3
Je keuze klopt als je de lus kiest, met als reden dat de lus de woorden één keer
langsloopt en per stap één telling bijwerkt, terwijl de comprehension voor elk
verschillend woord de hele lijst opnieuw langsloopt met count.
Stap 3: words_used_once(counts)¶
Geeft het aantal woorden dat precies één keer voorkomt. In week 1 was dat deze lus:
def words_used_once(counts):
"""Telt hoeveel woorden precies één keer voorkomen."""
n = 0
for word in counts:
if counts[word] == 1:
n = n + 1
return n
Schrijf dezelfde functie in één regel, met sum en een list comprehension.
Aanroep |
Resultaat |
|---|---|
|
|
|
|
Hint
Je hebt alleen de aantallen nodig, niet de woorden. counts.values() geeft je
de aantallen. Maak een lijst met een 1 voor elk aantal dat 1 is, en tel die
op.
# jouw oplossing
assert words_used_once({"spam": 2, "taart": 1}) == 1
assert words_used_once({}) == 0
assert (
words_used_once(count_words(word_list(read_text(TEKSTEN + "vuurtoren.txt")))) == 94
)
Stap 4: ngram_loop(word, n)¶
Een woord kun je opdelen in stukjes van n letters die elkaar overlappen. Voor
n = 2 zijn dat letterparen: "toren" wordt ["to", "or", "re", "en"].
Zoekmachines en spellingcontroles vergelijken woorden op zulke stukjes: een
woord met een tikfout deelt er nog de meeste met het bedoelde woord.
Deze functie doet het met een list comprehension:
def ngram(word, n):
"""Geeft de overlappende stukjes van n letters van word, op volgorde."""
return [word[i : i + n] for i in range(len(word) - n + 1)]
Schrijf ngram_loop(word, n), die precies hetzelfde doet, met een lus.
Aanroep |
Resultaat |
|---|---|
|
|
|
|
|
|
Hint
Beantwoord de vragen van het lusrecept. Wat je verzamelt en wat je teruggeeft,
zie je aan de vierkante haken. Wat je langsloopt, staat na for, en wat er per
stap bijkomt, staat vooraan.
# jouw oplossing
def ngram(word, n):
"""Geeft de overlappende stukjes van n letters van word, op volgorde."""
return [word[i : i + n] for i in range(len(word) - n + 1)]
assert ngram_loop("toren", 2) == ["to", "or", "re", "en"]
assert ngram_loop("toren", 5) == ["toren"]
assert ngram_loop("toren", 6) == []
for word in ["", "a", "vuurtoren"]:
for n in [1, 2, 3]:
assert ngram_loop(word, n) == ngram(word, n)
Stap 5: count_table(counts)¶
Geeft de telling als lijst van lijsten: voor elk woord een lijst [woord, aantal], in de volgorde van de dictionary.
Aanroep |
Resultaat |
|---|---|
|
|
|
|
# jouw oplossing
assert count_table({"spam": 2, "taart": 1}) == [["spam", 2], ["taart", 1]]
assert count_table({}) == []
assert count_table(count_words(word_list(read_text(TEKSTEN + "vuurtoren.txt"))))[0] == [
"de",
8,
]
Tot slot¶
In kort.txt zijn ik, taarten en en samen al 45 procent van de woorden.
In vuurtoren.txt haalt geen enkel woord meer dan 4 procent, naar beneden
afgerond zoals share doet, en de woorden die het hoogst scoren, zijn korte
woorden: een, de, en en het.
Kijk nog eens terug naar stap 2 en 3. Allebei tellen ze, en toch werd de ene een
lus en de andere een comprehension. Het verschil zit in wat er per stap
gebeurt: stap 2 werkt per woord een telling bij, stap 3 verzamelt alleen een
1 per aantal. Die vraag, wat gebeurt er per stap, beslist ook in de weken
hierna welke vorm je kiest.