In zijn Novembernummer van 2011 publiceerde NWT Magazine onderstaande Metrokaart der exacte wetenschap. De uitdaging van deze opgave bestaat erin om nieuwe gegevenstypes aan Python toe te voegen die toelaten om dergelijke metrokaarten op te stellen, uit te breiden en te bevragen. Een metrokaart is opgebouwd uit een aantal metrolijnen die verschillende metrostations in een vaste volgorde met elkaar verbinden. Het verkeer op een metrolijn verloopt dus in één richting, en bij deze opgave vormen metrolijnen ook nooit een lus. Sommige metrostations kunnen wel op verschillende metrolijnen liggen, zodat daar van de ene op de andere lijn kan overgestapt worden.
Definieer een klasse Station waarmee de metrostations op een metrokaart kunnen voorgesteld worden. Elk metrostation beschikt over de eigenschappen naam, jaar en omschrijving, die telkens verwijzen naar een string (het jaartal kan ook beschreven worden door bv. 6e eeuw v. Chr.). Vanuit elk metrostation kan je vertrekken naar nul of meer andere metrostations, maar elk van deze uitgaande verbindingen liggen op een verschillende metrolijn. Om deze uitgaande verbindingen voor te stellen, beschikt elk metrostation ook over een eigenschap lijnen die verwijst naar een dictionary. Deze dictionary geeft de metrolijnen aan die vertrekken vanuit het station, door telkens de naam van een metrolijn (een string; hier steeds de naam van een exacte wetenschap) af te beelden op het volgende metrostation op die lijn.
De objecten van de klasse Station moeten verder minstens over de volgende methoden beschikken:
Een initialisatiemethode __init__ met drie parameters: naam, jaar en omschrijving. Aan deze parameters moeten drie strings doorgegeven worden, die telkens moeten toegekend worden aan de gelijknamige eigenschappen van het nieuw aangemaakt object. Voorts moet elk nieuw aangemaakt object ook beschikken over een eigenschap lijnen die initieel verwijst naar een lege dictionary.
Een methode __eq__ die kan gebruikt worden om te testen of twee metrostations gelijk zijn. Twee metrostations worden enkel als gelijk beschouwd als de waarden van hun eigenschappen naam, jaar en omschrijving gelijk zijn. Merk dus op dat de waarde van de eigenschap lijnen geen invloed heeft op de gelijkheid van metrostations.
Een methode __repr__ die een stringvoorstelling van het metrostation teruggeeft volgens het formaat Station(naam=naam, jaar=jaar, omschrijving=omschrijving). Hierbij moeten naam, jaar en omschrijving respectievelijk ingevuld worden met de stringvoorstelling van de eigenschappen naam, jaar en omschrijving van het object.
Een methode __str__ die een stringvoorstelling van het metrostation teruggeeft volgens het formaat naam (jaar, omschrijving). Hierbij moeten naam, jaar en omschrijving respectievelijk ingevuld worden met de waarde van de eigenschappen naam, jaar en omschrijving van het object.
Een methode verbind die kan gebruikt worden om het metrostation op een gegeven metrolijn te verbinden met een ander metrostation (een object van de klasse Station). De naam van de metrolijn en het andere metrostation waarmee de verbinding moet gemaakt worden, moeten als argumenten aan de methode doorgegeven worden. De methode moet een AssertionError opwerpen met de boodschap station is reeds verbonden op lijn naam indien het metrostation reeds een uitgaande verbinding heeft voor de opgegeven metrolijn. Hierbij moet op de plaats van naam in de boodschap uiteraard de naam van de metrolijn ingevuld worden.
Een methode volgende waaraan de naam van een metrolijn moet doorgegeven worden. De methode moet het metrostation teruggeven waarmee het metrostation verbonden is op de aangegeven metrolijn. Indien het metrostation op de gegeven metrolijn niet verbonden is met een ander metrostation, dan moet de methode de waarde None teruggeven.
Definieer een klasse Metrokaart waarmee metrokaarten kunnen voorgesteld worden. Metrokaarten zijn opgebouwd uit een aantal metrolijnen die verschillende metrostations in een vaste volgorde met elkaar verbinden. Elke metrokaart beschikt over een eigenschap lijnen, die verwijst naar een dictionary. Deze dictionary beeldt voor elke metrolijn op de metrokaart de naam van de lijn (een string; hier steeds de naam van een exacte wetenschap) af op het beginstation van de metrolijn (een object van de klasse Station). De objecten van de klasse Metrokaart moeten verder minstens over de volgende methoden beschikken:
Een initialisatiemethode __init__ die ervoor moet zorgen dat elke nieuw aangemaakte metrokaart een eigenschap lijnen heeft die initieel verwijst naar een lege dictionary.
Een methode beginstation die het beginstation (een object van de klasse Station) van een gegeven metrolijn teruggeeft. De naam van de metrolijn moet als argument aan de functie doorgegeven worden. Indien de metrokaart geen metrolijn heeft met de opgegeven naam, dan moet de methode de waarde None teruggeven.
Een methode eindstation die het eindstation (een object van de klasse Station) van een gegeven metrolijn teruggeeft. De naam van de metrolijn moet als argument aan de functie doorgegeven worden. Indien de metrokaart geen metrolijn heeft met de opgegeven naam, dan moet de methode de waarde None teruggeven.
Een methode uitbreiden
waarmee een gegeven metrolijn kan uitgebreid worden met een nieuw
metrostation. De naam van de metrolijn en het nieuwe metrostation (een
object van de klasse Station)
moeten als argumenten aan de methode doorgegeven worden. Het uitbreiden
van een metrolijn gebeurt door het eindstation van de metrolijn te
verbinden met het nieuwe metrostation. Indien de metrokaart nog geen
metrolijn bevatte met de opgegeven naam, dan moet het nieuwe
metrostation het beginstation van de metrolijn worden. Maar let op: een
metrokaart mag nooit twee metrostations bevatten met dezelfde naam.
Indien de metrokaart reeds een metrostation heeft met dezelfde naam als
het nieuwe metrostation, dan moet het eindstation van de gegeven
metrolijn verbonden worden met het bestaande station.Veronderstel
onderstaande situatie waarbij op lijn1
reeds een metrostation voorkomt met naam B,
en we lijn2 (met
eindstation C) willen
uitbreiden met een metrostation met naam B.
Dan moet metrostation C
verbonden worden met het bestaande metrostation (hier aangeven in het
groen) en moet er geen nieuw metrostation aan de metrokaart toegevoegd
worden (hier aangegeven in het rood).
Een methode stationsToevoegen waaraan de locatie van een tekstbestand moet doorgegeven worden. Elke regel van dit bestand moet de volgende vier velden bevatten, telkens van elkaar gescheiden door één enkele tab: i) de naam van een exacte wetenschap, ii) de naam van een wetenschapper, iii) een jaaraanduiding en iv) een korte omschrijving van de belangrijkste verwezenlijkingen van de wetenschapper. De methode moet voor elke regel de metrolijn aangeduid met de naam van de exacte wetenschap uitbreiden met een metrostation waarvan de naam, het jaar en de omschrijving gebaseerd zijn op de laatste drie velden. Deze uitbreidingen moeten gebeuren in de volgorde waarin de regels in het bestand voorkomen.
Zorg ervoor dat de initialisatiemethode een optionele parameter stations heeft, waaraan de locatie van een tekstbestand kan doorgegeven worden. Als er een waarde doorgegeven wordt voor deze parameter, dan moet bij initialisatie de metrokaart reeds uitgebreid worden met de volgorde en beschrijving van de metrostations zoals ze in het tekstbestand vervat zitten. Dit doe je uiteraard door hiervoor de methode stationsToevoegen aan te roepen.
Zorg er bij de implementatie van de klassen Station en Metrokaart voor dat je telkens optimaal gebruik maakt van de methoden die je reeds eerder geïmplementeerd hebt.
>>> station1 = Station('James Watson', '1953', 'opheldering DNA-structuur')
>>> station1.naam
'James Watson'
>>> station1.jaar
'1953'
>>> station1.omschrijving
'opheldering DNA-structuur'
>>> station1.lijnen
{}
>>> station1 == station1
True
>>> station1
Station(naam='James Watson', jaar='1953', omschrijving='opheldering DNA-structuur')
>>> station2 = Station(naam='Ernst Mayr', jaar='1942', omschrijving='biologische soorten')
>>> print(station2)
Ernst Mayr (1942, biologische soorten)
>>> station1 == station2
False
>>> station1.verbind('biologie', station2)
>>> station1.lijnen
{'biologie': Station(naam='Ernst Mayr', jaar='1942', omschrijving='biologische soorten')}
>>> station1.volgende('biologie')
Station(naam='Ernst Mayr', jaar='1942', omschrijving='biologische soorten')
>>> print(station1.volgende('chemie'))
None
>>> station3 = Station('Marshall Warren Nirenberg', '1962', 'genetische code')
>>> station4 = Station('Ilya Prigogine', '1955', 'zelf-organisatie en niet-evenwicht')
>>> station1.verbind('chemie', station3)
>>> station1.verbind('chemie', station4)
Traceback (most recent call last):
AssertionError: station is reeds verbonden op lijn chemie
>>> station1.verbind('genomica', station4)
>>> station1.lijnen
{'biologie': Station(naam='Ernst Mayr', jaar='1942', omschrijving='biologische soorten'), 'genomica': Station(naam='Ilya Prigogine', jaar='1955', omschrijving='zelf-organisatie en niet-evenwicht'), 'chemie': Station(naam='Marshall Warren Nirenberg', jaar='1962', omschrijving='genetische code')}
>>> print(station1.volgende('chemie'))
Marshall Warren Nirenberg (1962, genetische code)
>>> print(station1.volgende('genomica'))
Ilya Prigogine (1955, zelf-organisatie en niet-evenwicht)
Bij onderstaande voorbeeldsessie gaan we ervan uit dat het tekstbestand mijlpalen.txt1 zich in de huidige directory bevindt.
>>> kaart = Metrokaart()
>>> kaart.uitbreiden('genomica', Station('James Watson', '1953', 'opheldering DNA-structuur'))
>>> kaart.uitbreiden('genomica', Station('Marshall Warren Nirenberg', '1962', 'genetische code'))
>>> kaart.uitbreiden('genomica', Station(naam='Frederick Sanger', jaar='1977', omschrijving='sequencing'))
>>> kaart.beginstation('genomica')
Station(naam='James Watson', jaar='1953', omschrijving='opheldering DNA-structuur')
>>> kaart.eindstation('genomica')
Station(naam='Frederick Sanger', jaar='1977', omschrijving='sequencing')
>>> kaart.uitbreiden('chemie', Station('Robert Boyle', '1661', 'The Sceptical Chymist'))
>>> kaart.beginstation('chemie')
Station(naam='Robert Boyle', jaar='1661', omschrijving='The Sceptical Chymist')
>>> kaart.eindstation('chemie')
Station(naam='Robert Boyle', jaar='1661', omschrijving='The Sceptical Chymist')
>>> kaart2 = Metrokaart('mijlpalen.txt')
>>> kaart2.beginstation('chemie')
Station(naam='Robert Boyle', jaar='1661', omschrijving='The Sceptical Chymist')
>>> kaart2.eindstation('chemie')
Station(naam='Paul Crutzen', jaar='1985', omschrijving='atmosfeerchemie')
>>> station = kaart2.beginstation('genomica')
>>> station
Station(naam='James Watson', jaar='1953', omschrijving='opheldering DNA-structuur')
>>> station.volgende('biologie')
Station(naam='Ernst Mayr', jaar='1942', omschrijving='biologische soorten')
>>> print(station.volgende('genomica'))
Marshall Warren Nirenberg (1962, genetische code)
>>> print(station.volgende('chemie'))
Ilya Prigogine (1955, zelf-organisatie en niet-evenwicht)
>>> station.volgende('genomica').volgende('genomica')
Station(naam='Frederick Sanger', jaar='1977', omschrijving='sequencing')
>>> kaart2.uitbreiden('chemie', Station('Martin Karplus', '2013', 'computationele chemie'))
>>> kaart2.uitbreiden('informatica', Station('Martin Karplus', '2013', 'computationele chemie'))
>>> station = kaart2.eindstation('chemie')
>>> station
Station(naam='Martin Karplus', jaar='2013', omschrijving='computationele chemie')
>>> kaart2.uitbreiden('chemie', Station('Michael Levitt', '2014', 'computationele chemie'))
>>> kaart2.uitbreiden('informatica', Station('Arieh Warshel', '2014', 'computationele chemie'))
>>> station.lijnen
{'informatica': Station(naam='Arieh Warshel', jaar='2014', omschrijving='computationele chemie'), 'chemie': Station(naam='Michael Levitt', jaar='2014', omschrijving='computationele chemie')}