Een register bij een tekst

Deze pagina bevat uitvoerbare code.

Opdracht: Een register bij een tekst

De gegeven code uit het practicum:

# Dit notebook staat in solutions/ en de teksten in problems/assets/teksten/,
# vandaar het pad terug, net als in het practicum.
TEKSTEN = "../problems/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()


EXAMPLE = (
    "De vuurtoren draait.\nHet licht draait elke nacht.\nDe schipper ziet het licht."
)

Stap 1: index_words(words, n)

def index_words(words, n):
    """Geeft de woorden van minstens n letters, elk één keer, op volgorde."""
    result = []

    for word in words:
        if len(word) >= n and word not in result:
            result.append(word)

    return result


assert index_words(["de", "licht", "draait", "licht"], 5) == ["licht", "draait"]
assert index_words([], 5) == []
assert index_words(word_list(EXAMPLE), 5) == [
    "vuurtoren",
    "draait",
    "licht",
    "nacht",
    "schipper",
]

word not in result kijkt naar wat de lus tot nu toe heeft verzameld. Dat is precies wat een comprehension niet kan. De set comprehension {word for word in words if len(word) >= n} levert dezelfde woorden op, maar zonder volgorde:

{word for word in word_list(EXAMPLE) if len(word) >= 5} == set(
    index_words(word_list(EXAMPLE), 5)
)
True

Stap 2: lines_with(word, lines)

def lines_with(word, lines):
    """Geeft de regelnummers, vanaf 1, van de regels in lines waarop word staat."""
    return [nr for nr, line in enumerate(lines, 1) if word in word_list(line)]


assert lines_with("draait", EXAMPLE.split("\n")) == [1, 2]
assert lines_with("taart", EXAMPLE.split("\n")) == []
assert lines_with("licht", []) == []

nr is het regelnummer en line de regel zelf. Het filter vraagt aan de lijst woorden of word erin staat, niet aan de string: "licht" in "lichten" is True, "licht" in ["lichten"] niet.

Stap 3: register(text, n)

def register(text, n):
    """Geeft bij elk woord van minstens n letters de regels waarop het staat."""
    lines = text.split("\n")
    return {word: lines_with(word, lines) for word in index_words(word_list(text), n)}


assert register(EXAMPLE, 5) == {
    "vuurtoren": [1],
    "draait": [1, 2],
    "licht": [2, 3],
    "nacht": [2],
    "schipper": [3],
}
assert register("", 5) == {}
assert len(register(read_text(TEKSTEN + "vuurtoren.txt"), 8)) == 19

lines staat in een eigen variabele, zodat de tekst maar één keer in regels wordt opgedeeld en niet opnieuw voor elk woord. De volgorde van het register is die van index_words, omdat een dictionary de volgorde aanhoudt waarin de sleutels erin komen.

Stap 4: on_more_lines(reg)

def on_more_lines(reg):
    """Geeft de set van woorden uit het register die op meer dan één regel staan."""
    return {word for word, nrs in reg.items() if len(nrs) > 1}


assert on_more_lines(register(EXAMPLE, 5)) == {"draait", "licht"}
assert on_more_lines({}) == set()
assert on_more_lines(register(read_text(TEKSTEN + "vuurtoren.txt"), 8)) == {
    "vuurtoren",
    "schipper",
    "vijftien",
    "seconden",
}

Stap 5: print_register(reg)

def print_register(reg):
    """Drukt per woord uit het register een regel af met zijn regelnummers."""
    for word, nrs in reg.items():
        print(word, nrs)


print_register(register(EXAMPLE, 5))
print_register(register(read_text(TEKSTEN + "vuurtoren.txt"), 8))
vuurtoren [1]
draait [1, 2]
licht [2, 3]
nacht [2]
schipper [3]
vuurtoren [1, 10, 12]
zesenvijftig [1]
tweehonderdzesendertig [2]
windstil [3]
zeegaten [5]
gevaarlijk [5]
zandbanken [5]
verschuiven [6]
schipper [7, 11]
versiering [10]
vijftien [12, 22]
seconden [12, 22]
vuurtorenwachter [15]
brandend [16]
spiegels [16]
gebleven [18]
vertellen [18]
geschiedenis [18]
tegenwoordig [23]

Van de negentien woorden staan er vier op meer dan één regel. vuurtoren staat op drie regels; vuurtorenwachter telt niet mee, omdat word_list hele woorden vergelijkt.