Funktionale Programmierung mit Haskell/ Beispielprogramme/ Wochentagsbestimmung

In diesem Programm wird aus einem Tagesdatum der zugehörige Wochentag ermittelt. Dies geschieht mittels der Gaußschen Wochentagsformel.

Dateiname wochentag.hs
{- Programm zur Errechnung von Wochentagen -}

import Data.List.Split  (splitOn)
import Data.Char        (isNumber)

woTag = ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"]
	
wFormel :: Int -> Int -> Int-> String
wFormel d m y = x
        where
         x  = woTag!!(z0::Int)
         z0 = mod z1 7
         z1 = d + z2 + z3 + truncate(fromIntegral (z3) / 4) 
              + truncate(fromIntegral (z4)/4 ) - (2*z4)
         z2 = [2,5,0,3,5,1,4,6,2,4,0,3]!!(m'-1)	
         z3 = if m<3 then (mod y 100) -1
                     else (mod y 100)
         m' = if m<3 then m+10 else m-2
         z4 = if m<3 then truncate(fromIntegral (y-1) / 100)
                     else truncate(fromIntegral y / 100)
              
evaluiereList :: [String] -> String
evaluiereList datList
         | length(datList) /= 3               = "Falsche Punktsetzung"
         | length (datList!!0) < 1            = "Tagesangabe ist zu kurz"
         | length (datList!!0) > 2            = "Tagesangabe ist zu lang"
         | length (datList!!1) < 1            = "Monatsangabe ist zu kurz"
         | length (datList!!1) > 2            = "Monatsangabe ist zu lang"
         | length (datList!!2) < 4            = "Jahresangabe ist zu kurz"
         | length (datList!!2) > 4            = "Jahresangabe ist zu lang"
         | not (all isNumber (datList!!0))    = "Tagesangabe ist keine Zahl"
         | not (all isNumber (datList!!1))    = "Monatsangabe ist keine Zahl"
         | not (all isNumber (datList!!2))    = "Jahresangabe ist keine Zahl"
         | otherwise                          = "OK"

evaluiereDat :: Int -> Int -> Int-> String
evaluiereDat dd mm yy
         | dd<1                 = "Tagesangabe ist kleiner als Eins"
         | dd>31                = "Tagesangabe ist groesser als 31"
         | mm<1                 = "Monatsangabe ist kleiner als Eins"
         | mm>12                = "Monatsangabe ist groesser als 12"
         | yy<1582              = "Jahresangabe ist kleiner als 1582"
         | yy>2999              = "Jahresangabe ist groesser als 2999"
         | (dd==31)&&(elem mm [2,4,6,9,11]) = "Dieser Monat hat nur 30 Tage"
         | (mm==2)&&(dd==30)     = "Den 30. Februar gibt es nicht"
         | (mm==2)&&(dd==29)&&((mod yy 400) ==0) = "OK"
         | (mm==2)&&(dd==29)&&((mod yy 100) ==0) = 
               ("Das Jahr " ++ show yy ++ " war/ist kein Schaltjahr")
         | (mm==2)&&(dd==29)&&((mod yy 4)    >0) = 
               ("Das Jahr " ++ show yy ++ " war/ist kein Schaltjahr")
         | otherwise            = "OK"

main = do 
          putStrLn "Bitte ein Datum eingeben (dd.mm.yyyy)"
          datString <- getLine
          let datList = splitOn "." datString
              ok = evaluiereList datList
          if ok /= "OK" 
            then do
                 putStrLn ("FEHLER bei der Eingabe: " ++ ok)
                 return ()
            else do
              let dd = read ((splitOn "." datString)!!0)::Int
                  mm = read ((splitOn "." datString)!!1)::Int
                  yy = read ((splitOn "." datString)!!2)::Int
                  ok = evaluiereDat dd mm yy
              if ok /= "OK" 
              then putStrLn ("FEHLER beim Datum: " ++ ok)
              else putStrLn ("Der " ++ datString ++ " war/ist ein " ++ (wFormel dd mm yy))
          main
--Ende des Programms

Erläuterungen:

  • Das Programm benötigt die Bibliotheken Data.List.Split für splitOn und Data.Char für isNumber. Dann wird die Liste der Wochentage definiert.
  • Die Typisierung der Funktionen wFormel, evaluiereList und evaluiereDat könnte man auch weglassen, denn Haskell erkennt sie sowieso (sie würden dann allerdings komplizierter aussehen).
  • Das Hauptprogramm main nimmt die Eingabe entgegen und splittet sie an den Punkten in eine Liste mit Strings auf ([String]). Diese Strings sind dafür gedacht, zu Integer-Werten (Tag, Monat und Jahr) verarbeitet zu werden, aber sie könnten auch völlig unplausible Werte enthalten. Die Funktion evaluiereList sorgt dafür, dass unplausible Werte erkannt werden, in diesem Fall wird ein FEHLER bei der Eingabe ausgegeben.
  • Danach können die Werte für Tag, Monat und Jahr ermittelt werden, aber auch sie könnten unplausibel sein. Diese unplausiblen Datumswerte erkennt die Funktion evaluiereDat und gibt bei Bedarf einen FEHLER bem Datum aus.
  • Schließlich erfolgt die Berechnung in wFormel und die Ausgabe des Wochentags in main. main ist bewusst die einzige Funktion, die I/O-Operationen durchführt, die anderen Funktionen sind "pure Haskell".
  • Mit dem Befehl main am Ende des Programms springt man wieder an den Anfang für die nächste Eingabe. Ist dies nicht gewünscht, lässt man diese Zeile weg. Das Programm muss dann mit einem Interrupt (Control-C) verlassen werden.

Zur Laufzeit verhält sich das Programm so:

Ausführung eines Programms wochentag.hs im Interpreter
$> runhaskell wochentag.hs 
Bitte ein Datum eingeben (dd.mm.yyyy)
unsinniger Wert
FEHLER bei der Eingabe: Falsche Punktsetzung
Bitte ein Datum eingeben (dd.mm.yyyy)
3.4
FEHLER bei der Eingabe: Falsche Punktsetzung
Bitte ein Datum eingeben (dd.mm.yyyy)
z.12.2000
FEHLER bei der Eingabe: Tagesangabe ist keine Zahl
Bitte ein Datum eingeben (dd.mm.yyyy)
10.10.10
FEHLER bei der Eingabe: Jahresangabe ist zu kurz
Bitte ein Datum eingeben (dd.mm.yyyy)
5.Oktober 1910
FEHLER bei der Eingabe: Falsche Punktsetzung
Bitte ein Datum eingeben (dd.mm.yyyy)
30.2.2000
FEHLER beim Datum: Den 30. Februar gibt es nicht
Bitte ein Datum eingeben (dd.mm.yyyy)
29.2.2003
FEHLER beim Datum: Das Jahr 2003 war/ist kein Schaltjahr
Bitte ein Datum eingeben (dd.mm.yyyy)
12.88.2000
FEHLER beim Datum: Monatsangabe ist groesser als 12
Bitte ein Datum eingeben (dd.mm.yyyy)
1.1.1
FEHLER bei der Eingabe: Jahresangabe ist zu kurz
Bitte ein Datum eingeben (dd.mm.yyyy)
10.10.2010
Der 10.10.2010 war/ist ein Sonntag
Bitte ein Datum eingeben (dd.mm.yyyy)
^C$>