Praktische Informatik 3 WS 2009/10 3. Übungsblatt Ausgabe: 12.11.2009 Abgabe: 26.11.2009 Berthold Hoffmann <hof> Dominik Dietrich <dodi> Christian Maeder <maeder> Julia Seiter <jseiter> Jasper van de Ven <jasper> Spaß ist Spaß, und Ernst ist Ernst. Und Fußball ist todernst! Nur weil der Server, mit dem die Spielpläne der Fußball-Bundesliga erstellt werden, abgeraucht ist und die Quellen der Cobol-Programme, mit denen sie erstellt wurden, nicht mehr zu rekonstruieren sind, droht die Bundesliga-Rückrunde ins Wasser zu fallen! In Haskell lässt sich so ein Plan zum Glück schnell errechnen. 6 Spielpläne erstellen 5 Punkte Entwickeln Sie ein Programm, mit dem ein Spielplan für eine Liga mit einer geraden Anzahl n > 0 von Mannschaften erstellt wird. Repräsentieren Sie die Mannschaften durch Zahlen von 0 bis n − 1.1 Ein Spieltag ist eine Liste von Paaren, in der jede Mannschaft genau einmal vorkommt. Dabei steht ein in Paar (i, j) für ein Heimspiel der Mannschaft i gegen die Gastmannschaft j. Ein Spielplan ist eine Liste von Spieltagen, in dem jede Mannschaft genau einmal gegen jede andere antritt und jede Mannschaft nicht mehr als n ÷ 2 Heim- oder Auswärtsspiele hat. Die Paarungen werden durch Rotation wie folgt bestimmt: Der erste Spieltag besteht aus den Paarungen: H G H ... ... 0 1 2 ... n÷ 2 −1 n− 1 n− 2 n −3 ... n÷2 Die Buchstaben H und G in der ersten Zeile geben an, ob die Teams in der zweiten Zeile Heimrecht oder Gastrecht haben. Die oben genannten Mannschaften haben also abwechselnd Heimrecht bzw. Gastrecht. Als Tupel wird also eine Spalte H i j als (i, j) und eine Spalte G i j als (j, i) dargestellt. Der erste Spieltag soll berechnet werden mit einer Funktion day :: Int → [(Int,Int)] Rotiert wird ein Spieltag wie folgt: 1. Team n − 1 bleibt an der gleichen Stelle (links unten). 2. In allen anderen Stellen der Tabelle wird Team i durch Team i + 1‘mod‘(n − 1) ersetzt. 3. In der ersten Spalte ändert sich bei einer Rotation Heim- zu Gastrecht oder umgekehrt. 4. In allen anderen Spalten bleibt das Heim- bzw. Gastrecht unverändert. Der zweite Spieltag sieht also wie folgt aus: G G H ... ... 1 2 3 ... n÷2 n −1 0 n −2 ... n÷ 2+ 1 1 Dahinter steht die kaufmännische Hoffnung, dass auch die Server für die Spielplanberechnungen in den Handball-, Basketball- und Eishockeyligen irgend wann einmal ihren Geist aufgeben werden und Sie dann auch für n 6= 18 schnell eine Lösung anbieten können. Die Rotation soll berechnet werden mit einer Funktion nextDay :: [(Int,Int)] → [(Int,Int)] Den Spielplan für eine Hin- oder Rückrunde mit n − 1 Spieltagen erhält man durch n − 2 Rotationen des ersten Spieltags: plan :: Int → [[(Int,Int)]] Dies ergibt eine abstrakte Funktion für das Erstellen von Spielplänen mit einer geraden Anzahl n von Mannschaften. 7 Der Bundesliga-Spielplan 5 Punkte Nun geht es darum, aus der abstrakten Planung einen schön gedruckten Spielplan für die Bundesliga zu erstellen.2 Erstellen Sie eine Liste theTeams ::Teams, in der die Namen aller Bundesligavereine enthalten sind, wobei Teams folgendes Typsynonym ist: type Teams = [String] Implementieren Sie nun eine Funktion theFinalPlan ::Teams →String, die für die Mannschaften einen Plan erstellt und formatiert ausgibt. Die Anzahl n der Mannschaften lässt sich aus der Liste berechnen – ist sie ungerade, soll eine Mannschaft "-- spielfrei --" hinzugefügt werden. Definieren Sie für die Formatierung folgende Funktionen: 1. Die Funktionen theHomeTeam, theGuestTeam ::Int →String →String formatieren Heimmannschaften linksbündig und Gastmannschaften rechtsbündig in einer Zeichenkette der Länge b (dem ersten Argument). 2. Die Funktion theMatch ::Int →Teams →(Int,Int) →String formatiert eine Paarung; dabei steht die Heimmannschaft vorne. 3. Die Funktion theDay ::Int → Teams →[(Int,Int)] →String formatiert einen Spieltag (mit abschließender Leerzeile, ggf. auch mit Überschrift). 4. Die Funktion thePlan ::Int →Teams →[[(Int,Int)]] →String formatiert den Spielplan. Tipps für die Implementierung: 1. Die Funktion (!!) :: [a] → Int → a selektiert das Elemente einer Liste ℓ mit den Indices 0 . . . (length ℓ) − 1. 2. Benutzen die Bibliotheksfunktion unlines ::[String] →String, um eine Liste von Zeichenketten in eine Zeichenkette mit Zeilenwechseln umzuwandeln. 3. Die Aktion putStrLn ::String →IO() gibt – als zuletzt aufgerufene Funktion eines Ausdrucks – eine Zeichenkette mit Zeilenwechseln und ohne die Gänsefüßchen aus. [Die Idee zu dieser Aufgabe ist von Christian Maeder.] Dies ist Fassung 2 vom 10. November 2009. 2 Fans anderer Fußballligen oder anderer Mannschaftssportarten dürfen statt dessen auch andere Spielpläne errechnen.