Tekst genereren met Markovprocessen¶
Opdracht: Tekst genereren met Markovprocessen
Opdracht 1: De functie create_dictionary¶
import random
# Dit notebook staat in solutions/ en het bestand in practicals/assets/,
# vandaar het pad terug. In de opgave zelf is dat gewoon "a.txt".
A_TXT = "../practicals/assets/a.txt"
def create_dictionary(filename):
"""Geeft de woordovergangen uit een UTF-8-tekstbestand als dictionary terug."""
with open(filename, encoding="utf-8") as file:
text = file.read()
words_follow = {}
previous_word = "$"
for new_word in text.split():
if previous_word not in words_follow:
words_follow[previous_word] = [new_word]
else:
words_follow[previous_word] += [new_word]
if new_word[-1] in ".?!":
previous_word = "$"
else:
previous_word = new_word
return words_follow
assert create_dictionary(A_TXT) == {
"krijg": ["toch"],
"voor": ["de"],
"wil": ["taarten", "42"],
"toch": ["spam"],
"Ik": ["wil", "krijg", "wil"],
"spam": ["en"],
"42": ["en", "taarten!"],
"$": ["Ik", "Ik", "Ik"],
"taarten": ["en", "voor"],
"de": ["vakantie?"],
"en": ["42", "spam.", "taarten"],
}
previous_word begint bij het startsymbool "$", en elk woord dat op een
zin-einde volgt (., ? of !) zet previous_word daar weer op terug. Zo
komt elk zinsbegin in de lijst bij "$" terecht, precies zoals het college
en de opgave het beschrijven.
De test hierboven is exact het voorbeeld uit de opgave: create_dictionary('a.txt')
levert de dictionary op die daar staat.
Opdracht 2: De functie generate_text¶
def generate_text(words_follow, n_words):
"""Drukt n_words woorden af volgens de woordovergangen in words_follow.
n_words is positief. Begint een nieuwe zin na een eindleesteken of een
woord zonder opvolger; de dictionary bevat daarvoor het startsymbool '$'.
"""
previous_word = "$"
words = []
for _ in range(n_words):
new_word = random.choice(words_follow[previous_word])
words.append(new_word)
if new_word[-1] in ".?!" or new_word not in words_follow:
previous_word = "$"
else:
previous_word = new_word
print(" ".join(words))
generate_text(create_dictionary(A_TXT), 20)
# Controleer de afgedrukte tekst zonder een willekeurige zin vast te leggen.
from contextlib import redirect_stdout
from io import StringIO
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as directory:
training_file = Path(directory) / "training.txt"
training_file.write_text("A B C", encoding="utf-8")
transitions = create_dictionary(training_file)
for length, expected in [(3, "A B C"), (4, "A B C A")]:
output = StringIO()
with redirect_stdout(output):
generate_text(transitions, length)
assert output.getvalue().strip() == expected
training_file.write_text("één café.", encoding="utf-8")
assert create_dictionary(training_file) == {"$": ["één"], "één": ["café."]}
training_file.write_text("A B A. A B C. B A C. C C C.", encoding="utf-8")
transitions = create_dictionary(training_file)
assert transitions == {
"$": ["A", "A", "B", "C"],
"A": ["B", "B", "C."],
"B": ["A.", "C.", "A"],
"C": ["C", "C."],
}
output = StringIO()
with redirect_stdout(output):
generate_text(transitions, 20)
generated = output.getvalue().split()
assert len(generated) == 20
assert "$" not in generated
previous_word = "$"
for word in generated:
assert word in transitions[previous_word]
if word[-1] in ".?!" or word not in transitions:
previous_word = "$"
else:
previous_word = word
Ik wil taarten voor de vakantie? Ik wil taarten voor de vakantie? Ik wil taarten en taarten voor de vakantie?
Deze uitwerking kiest de toegestane herstart bij $ als een woord geen
opvolger heeft. De functie blijft het gevraagde aantal woorden afdrukken.
Door de willekeur kun je bij algemene invoer geen vaste zin verwachten.
Je kunt wel het aantal woorden, het startsymbool en de geldige overgangen
controleren. De assertions hierboven doen dat voor de practicumtekst en toetsen
ook het lezen van UTF-8. Bij A B C is er steeds maar één keuze: drie woorden
geven A B C, vier woorden geven na de herstart A B C A.