In 2009 schreef de gelauwerde dichteres Wendy Cope in opdracht van de Daily Telegraph1 het prachtige liefdesgedicht Another Valentine. Hierin gaat ze op onderzoek naar de grond van de kritiek die vaak te beurt valt aan de meest romantische dag van het jaar. Terwijl de verteller uit het gedicht mijmert over de verplichtingen van Valentijn, wordt ze zelf overvallen door romantische gevoelens.
Today we are obliged to be romantic
And think of yet another valentine.
We know the rules and we are both pedantic:
Today's the day we have to be romantic.
Our love is old and sure, not new and frantic.
You know I'm yours and I know you are mine.
And saying that has made me feel romantic,
My dearest love, my darling valentine.
De aantrekkingskracht van de liefde wordt nog versterkt door een wel heel bijzondere eigenschap van het gedicht. Kies een willekeurig woord uit de eerste drie regels, tel de letters van dit woord, en spring het overeenkomstig aantal woorden vooruit. Stel bijvoorbeeld dat je begint bij het woord obliged op de eerste regel: dit woord bestaat uit 7 letters, dus spring je 7 woorden vooruit naar het woord yet op de tweede regel. Tel het aantal letters van dat woord, en blijf op die manier vooruitspringen tot je niet meer verder kan. Welk woord op de eerste drie regels je ook kiest als vertrekpunt, je komt altijd uit bij het woord love op de laatste regel. Je kunt dit nagaan door met de muis over de woorden van het gedicht te bewegen. Op die manier kan je ook zien dat als je vertrekt vanaf het woord day op de vierde regel, je finaal uitkomt bij het woord romantic op de voorlaatste regel.
Deze bijzondere eigenschap is echter minder toevallig dan je op het eerste gezicht misschien zou vermoeden — ze is gebaseerd op een principe dat Kruskal's telprocedure genoemd wordt. Deze telprocedure werd door de fysicus Martin Kruskal van Rutgers University voor het eerst gebruikt in een kaarttruc, waarmee hij goochelaars van over de hele wereld introduceerde in de wiskundige theorie van de Markovketens2. Zowel bij de kaarttruc als bij het liefdesgedicht komt het erop neer dat verschillende vertakkingen samenkomen in een gemeenschappelijke stroom die uitmondt in een voorspelbare bestemming. We testen deze telprocedure verder uit op de eerste drie verzen van het bijbelboek Genesis:
In the beginning God created the heaven and the earth.
And the earth was without form, and void;
and darkness was upon the face of the deep.
And the Spirit of God moved upon the face of the waters.
And God said, Let there be light: and there was light.
Kies ook hier een willekeurig woord uit het eerste vers, tel de letters van dit woord, en spring het overeenkomstig aantal woorden vooruit. Stel bijvoorbeeld dat je start bij het woord beginning: dat woord bestaat uit 9 letters, dus spring je 9 woorden vooruit naar het eerste voorkomen van het woord the in het tweede vers. Tel het aantal letters van dat woord, en blijf op die manier vooruitspringen tot je een woord van het derde vers bereikt. Welk woord uit het eerste vers je ook kiest als vertrekpunt, alle wegen leiden uiteindelijk naar het woord God in het derde vers. Het werkt ook op dit bekende Engelse slaapliedje:
Twinkle, twinkle, little star,
How I wonder what you are.
Up above the world so high,
Like a diamond in the sky.
Twinkle, twinkle, little star,
How I wonder what you are.
Kies een willekeurig woord op de eerste twee regels, tel het aantal letters, en spring dat aantal woorden vooruit. Als je bijvoorbeeld het woord star kiest, dat bestaat uit vier letters, dan spring je vier woorden vooruit naar het woord what op de tweede regel. Als je dit blijft volhouden, dan is het laatste woord waar je naartoe kan springen altijd het woord you. Het laatste voorbeeld werd gevonden door Martin Gardner in de openingszinnen van Alice's Adventures in Wonderland:
Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do; once or twice she had peeped into the book her sister was reading.
De eerste 26 woorden leiden allemaal naar het woord sister op het einde van dit tekstfragment.
Ga voor de tekst in een gegeven tekstbestand na te gaan of de truc werkt. Bij het opsplitsen van een tekst in woorden leggen we vast dat de woorden bestaan uit de langst mogelijke opeenvolging van letters (hoofdletters of kleine letters). Dat betekent dus bijvoorbeeld dat de string Today's twee woorden bevat: Today en s. Gevraagd wordt:
Schrijf een functie splits waaraan een string (str) moet doorgegeven worden. De functie moet een lijst (list) teruggeven met alle opeenvolgende woorden (str) uit de gegeven string.
Schrijf een functie reeks waaraan een reeks (list of tuple) met woorden (str) moet doorgegeven worden. De functie heeft ook nog een tweede optionele parameter start waaraan een natuurlijk getal (int) kan doorgegeven worden (standaardwaarde: 0). De functie moet een tuple met twee elementen teruggeven. Het eerste element is de lijst (list) van woorden (str) die men bezoekt als men Kruskal's telprocedure toepast op de gegeven reeks woorden, te beginnen met het woord op de positie die werd doorgegeven aan de parameter start. Het tweede element is een natuurlijk getal (int) dat aangeeft hoeveel woorden men maximaal nog aan de gegeven reeks zou kunnen toevoegen, zonder dat nog naar een volgend woord kan gesprongen worden als men Kruskal's telprocedure toepast op het woord op de gegeven startpositie.
In bovenstaande figuur illustreren we de werking van de functie reeks op de woorden uit de eerste regel van het liefdesgedicht. Als we starten op positie 1, dan wordt de lijst met woorden die in het geel aangeduid zijn teruggegeven als eerste element van het tuple. Vanaf het woord obliged zouden we 7 woorden moeten vooruitspringen naar een volgende woord, wat vier woorden voorbij het laatste woord van de gegeven reeks woorden ligt. Er zouden dus nog 3 woorden aan de reeks kunnen toegevoegd worden zonder dat naar een volgende woord kan gesprongen worden, wat de waarde is die als tweede element van het tuple moet teruggegeven worden.
Schrijf een functie kruskal waaraan de locatie (str) van een tekstbestand moet doorgegeven worden. De functie heeft ook nog een tweede optionele parameter start waaraan een natuurlijk getal (int) kan doorgegeven worden (standaardwaarde: 0). De functie heeft ook nog een derde optionele parameter laatste waaraan een Booleaanse waarde (bool) kan doorgegeven worden (standaardwaarde: False). De functie moet de lijst (list) met woorden (str) teruggeven die men bezoekt als men Kruskal's telprocedure toepast op de tekst in het gegeven bestand, te beginnen met het woord op de positie die werd doorgegeven aan de parameter start. Hierbij worden de woorden genummerd vanaf 0. Als de waarde False werd doorgegeven aan de parameter laatste, dan moet Kruskal's telprocedure worden toegepast tot men niet meer naar een volgend woord kan springen. Als de waarde True werd doorgegeven aan de parameter laatste, dan moet Kruskal's telprocedure worden toegepast tot het eerste woord bereikt wordt op de laatste regel. Wanneer in dit laatste geval geen woord bereikt wordt op de laatste regel, dan moet de functie een AssertionError opwerpen met de boodschap geen woord bereikt op laatste regel.
Schrijf een functie truc waaraan de locatie (str) van een tekstbestand moet doorgegeven worden. De functie heeft ook nog een optionele parameter laatste (standaardwaarde: False) met dezelfde betekenis als bij de functie kruskal. De functie moet een Booleaanse waarde (bool) teruggeven, die aangeeft of alle woorden op de eerste regel in het gegeven tekstbestand finaal eindigen op eenzelfde woord als men Kruskal's telprocedure toepast zoals bij de functie kruskal. Merk dus op dat het ook mogelijk is dat deze functie een AssertionError moet opwerpen met de boodschap geen woord bereikt op laatste regel.
Bij onderstaande voorbeeldsessie gaan we ervan uit dat de tekstbestanden cope.txt3, genesis.txt4, twinkle.txt5 en alice.txt6 zich in de huidige directory bevinden.
>>> splits('Today we are obliged to be romantic')
['Today', 'we', 'are', 'obliged', 'to', 'be', 'romantic']
>>> splits('And think of yet another valentine.')
['And', 'think', 'of', 'yet', 'another', 'valentine']
>>> splits('We know the rules and we are both pedantic:')
['We', 'know', 'the', 'rules', 'and', 'we', 'are', 'both', 'pedantic']
>>> splits("Today's the day we have to be romantic.")
['Today', 's', 'the', 'day', 'we', 'have', 'to', 'be', 'romantic']
>>> woorden = ['Today', 'we', 'are', 'obliged', 'to', 'be', 'romantic']
>>> reeks(woorden)
(['Today', 'be'], 0)
>>> reeks(woorden, 1)
(['we', 'obliged'], 3)
>>> reeks(woorden, start=2)
(['are', 'be'], 0)
>>> kruskal('cope.txt7', 3)
['obliged', 'yet', 'We', 'the', 'we', 'both', 'the', 'have', 'Our', 'old', 'not', 'frantic', 'I', 'know', 'And', 'has', 'feel', 'love']
>>> kruskal('cope.txt8', 21)
['pedantic', 'be', 'Our', 'old', 'not', 'frantic', 'I', 'know', 'And', 'has', 'feel', 'love']
>>> kruskal('cope.txt9', 25)
['day', 'to', 'romantic', 'new', 'You', 'm', 'yours', 'are', 'saying', 'romantic']
>>> kruskal('cope.txt10', 25, laatste=True)
Traceback (most recent call last):
AssertionError: geen woord bereikt op laatste regel
>>> truc('cope.txt11')
True
>>> truc('cope.txt12', laatste=True)
True
>>> truc('genesis.txt13')
True
>>> truc('genesis.txt14', laatste=True)
True
>>> truc('twinkle.txt15')
True
>>> truc('twinkle.txt16', laatste=True)
True
>>> truc('alice.txt17')
True
>>> truc('alice.txt18', laatste=True)
False