instance -> int

Werbung
Die Berechnung des Osterdatums
Tobias Barth
Wolfgang Erlenkotter
Frank Kammer
25. Mai 1998
Inhaltsverzeichnis
1 Einleitung
2 Die Erzeugung des Datentyps Date
1
1
3 Die Ableitung von Enum
3
4 Weitere Funktionen
5 Die Berechnung des Osterdatums
6 Die Programmdateien
4
4
5
2.1 Modizierung fur Ord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 Modizierung von Num . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1 Die Funktion toI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2 Die Funktion toD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3 Die Listenfunktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.1 Dates.hs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.2 Ostern.hs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2
2
3
3
4
5
9
1 Einleitung
Die eigentliche Aufgabe bestand lediglich aus der Konstruktion eines Datentyps Date in Haskell. Wenn
man aber diese Funktionen geschrieben hat, bieten sich einige weitergehende Datumsberechnungen an:
die Bestimmung der Kalenderwoche, des Wochentags und die Berechnung einiger christlicher Feiertage,
die sich aus dem Osterdatum ableiten lassen. Dies sei im folgenden dargestellt.
2 Die Erzeugung des Datentyps Date
Der Datentyp Date wird in unserem Fall von den Klassen: Eq,
das alles?
1
Show, Ord, Num, Enum
abgeleitet. Wozu
Eq
Ord
Show
Num
Enum
Damit kann die Gleich- bzw. Ungleichheit festgestellt werden.
Ist ein Datum groer oder vielleicht auch kleiner?
Ich mochte mein Ergebnis angezeigt bekommen!
Hier wird sichergestellt, da ich mit den Datumswerten rechnen kann.
Das war schlielich die Aufgabe. Date ist ein Aufzahlungstyp.
2.1 Modizierung fur
instance Ord Date
compare x y |
|
|
x
x
x
x
<=
<
>=
>
where
toI x == toI y
toI x <= toI y
otherwise = GT
y
y
y
y
max x y
min x y
Ord
=
=
=
=
|
|
|
|
= EQ
= LT
compare
compare
compare
compare
toI x >= toI y
otherwise
= y
toI x <= toI y
otherwise
= y
x
x
x
x
y
y
y
y
/=
==
/=
==
GT
LT
LT
GT
= x
= x
Damit eine Ordnung (Reihenfolge) erkannt werden kann, mu der Typ Date von Ord abgeleitet werden.
Dabei kann die Denition aus der Prelude.hs ubernommen werden. Die einzige Aktualisierung ist, da
zuerst die Umwandlung des Datumstyps in eine Zahl geschehen mu.
2.2 Modizierung von Num
Um Datumswerte zu addieren, subtrahieren bzw. z. B. ein Int auf ein Date zu addieren, ist es auch
notwendig, Date von Num abzuleiten und extra Funktionen fur dieses zu schreiben: Die Modikation von
Num:
instance Num Date where
(+)
(-)
fromInt
= addDate -- jetzt kann man Tage addieren!
= subDate -- und subtrahieren
= toD
...
addDate a b =toD ((toI a) + (toI b))
subDate a b = toD (toI a - toI b)
ist deshalb notwendig, da ja einer der beiden Summanden vom Typ Integer sein kann. Bei der
Addition (Subtraktion) wandelt Hugs dann automatisch den Integerwert in ein Datumswert um. Prima,
oder?
fromInt
2
3 Die Ableitung von Enum
Nachdem nun Date bereits mehrfach uberladen wurde, machen wir es auch noch zur Instanz von Enum,
was ja schlielich die Aufgabe war. In Haskell sieht das dann so aus:
instance Enum Date where
fromEnum
= toI
toEnum
= toD
enumFrom
= fromD
enumFromThen = fromDT
fromEnum = toI
toEnum = toD
enumFrom = fromD
enumFromThen = fromDT
Wandelt einen Datumswert in einen Integerwert um.
Wandelt einen Integerwert in ein Datumswert um.
Erzeugt eine Liste, die mit dem angegebenen Datumswert beginnt:[n..]
Erzeugt eine Liste, derart: [n,m..]
3.1 Die Funktion toI
Die Umwandlung eines Datumswertes in eine Zahl ist nicht ganz so schwierig. Man berechnet die Anzahl
der Jahre seit (bzw. bis) 1970, die Anzahl der Monate und der Tage und schon hat man den dem Datumswert entsprechenden Integerwert.
Beispiele:
Dates> bspTag
Date{year=1998, month=5, day=16}
Dates> toI bspTag
10362
3.2 Die Funktion toD
Das war schlielich das schwierigste. Das Vorgehen hier:
1. Wieviel ganze Jahre "passen in\ die Zahl?
2. Wieviel ganze Monate, und
3. Wieviel Tage bleiben ubrig?
Dies leisten im einzelnen die Funktionen:
rechneJahr, rechneJahr_, rechneMJahr_, rechneMonat, rechneMonat_, rechneMonatM
Die Anwendung:
Dates> toD 10364
Date{year=1998, month=5, day=18}
Dates> toD (-5000)
Date{year=1956, month=4, day=24}
3
3.3 Die Listenfunktionen
Hier war es nur wichtig,
fromD a
= map toEnum [fromEnum a .. ]
fromDT a b = map toEnum [fromEnum a, fromEnum b .. ]
zu implementieren. Dann kann Hugs bzw. Haskell auch Listen von Datumswerten erzeugen.
Dates> [bspTag..]
[Date{year=1998, month=5, day=16}, Date{year=1998, month=5, day=17}, Date{year=
1998, month=5, day=18}, Date{year=1998, month=5, day=19}, Date{year=1998, month=
5,,{Interrupted!}
Dates> [mbspTag, bspTag..]
[Date{year=1950, month=7, day=20}, Date{year=1998, month=5, day=16}, Date{year=
2046, month=3, day=12}, Date{year=2094, month=1, day=6}, Date{year=2141, month=
11, day=3}, Date{year=2189, month=8, day=30}, Date{year={Interrupted!}
Damit war die eigentliche Aufgabe fertig.
4 Weitere Funktionen
Wenn ersteinmal die Hauptarbeit gemacht wurde, kann mit den Funktionen wTag der Wochentag und
mit kw die Kalenderwoche bestimmt werden. Beispiele:
Dates> kw (Date 1998 5 21)
21
Dates> wTag (Date 1998 5 21)
"Donnerstag"
5 Die Berechnung des Osterdatums
Gau entwickelte eine Formel, um das Osterdatum zu berechnen. Dabei nutzt er aus, da Ostern auf den
ersten Sonntag nach dem ersten Fruhjahrsvollmond fallt. In Haskell sieht das so aus:
-- Der Gausche Osteralgorithmus
ostern j = let h = div j 100
l = 4 + h - div h 4
m = 15 + h - div h 4 - div (8*h+13) 25
a = mod j 4
b = mod j 7
c = mod j 19
d = mod (19*c + m) 30
e = mod (2*a+4*b+6*d+l) 7
in
if (d==29 && e ==6) then (Date j 4 19)
else
if (d==28 && e==6 && c>=11) then (Date j 4 18)
4
else
if (22+d+e<=31) then (Date j 3 (22+d+e))
else
(Date j 4 (d+e-9))
Die restlichen Feiertage berechnen sich dann mit:
aschermittwoch j = (ostern j - 46)
pfingsten j = (ostern j + 49)
christiHim j = (ostern j + 39)
fronleichnam j = (ostern j + 60)
und eine U bersicht uber all` diese, bekommt man mit:
Ostern> feierTage 1998
[(" Aschermittwoch:", Date{year=1998, month=2, day=25}), (" Ostern: ", Date{year
=1998, month=4, day=12}), (" Christi Himmelfahrt: ", Date{year=1998, month=5, day
=21}), (" Pfingsten: ", Date{year=1998, month=5, day=31}), (" Fronleichnam: ",
Date{year=1998, month=6, day=11})]
Ostern>
6 Die Programmdateien
6.1 Dates.hs
------------------------------------------------------------------- Die Aufgabe von Blatt 4
-erstellt von
-- Wolfgang Erlenkoetter, Tobias Barth und Frank Kammer
----------------------------------------------------------------module Dates ( Date, toD, toI, bspTag, mbspTag, wTag,
distDays, kw, fst95, fst98) where
data Date = Date {year :: Int, month :: Int, day :: Int}
deriving (Eq, Show)
instance Ord Date
compare x y |
|
|
x
x
x
x
<=
<
>=
>
y
y
y
y
max x y
where
toI x == toI y
toI x <= toI y
otherwise = GT
=
=
=
=
= EQ
= LT
compare
compare
compare
compare
| toI x >= toI y
x
x
x
x
y
y
y
y
/=
==
/=
==
= x
5
GT
LT
LT
GT
min x y
| otherwise
= y
| toI x <= toI y
| otherwise
= y
= x
instance Num Date where
(+)
= addDate -- jetzt kann man Tage addieren!
(-)
= subDate -- und subtrahieren
fromInt
= toD
-----------------------------------------------------instance Enum Date where
fromEnum
= toI
toEnum
= toD
enumFrom
= fromD
enumFromThen = fromDT
-------------------------------------------------------- Umwandlung von einem Datum in eine fortlaufende Zahl
-- Diese Funktion berechnet die Anzahl der Tage zwischen
-- den beiden Jahren a und b, beide Jahre werden dabei
-- inklusive gerechnet.
-- a ist also das Startjahr und b das Endjahr
anzYDays a b = foldr (+) 0 (map (\x->(foldr (+) 0 (monthLengths x))) [a..b])
-- Die Funktion berechnet die Tage in einem Jahr, gegeben das Jahr,
-- der Monat und der Tag.
anzDDays j m t = foldr (+) 0 ( take (m-1) (monthLengths j)) + t
anzDDays_ j m t = (anzYDays j j)-(anzDDays j m t)+1
---- Die Funktion toI berechnet nun zu einem geg. Datum den Tag,
-- gerechnet ab bzw. bis 1.1.1970, dabei werden die eben definierten
-- Funktionen anzDDays und anzYDays benutzt.
toI :: Date -> Int
toI t = let jahr = year t
mon = month(t)
tag = day (t)
in
if jahr > 1969 then
(anzYDays 1970 (jahr-1))+(anzDDays jahr mon tag)-1
else
- ((anzYDays (jahr+1) 1969)+(anzDDays_ jahr mon tag))
----------------------------------------------------------------- Die Umwanldung einer fortlaufenden Zahl in ein gueltiges Datum
-- erst mal die Hilfe-Funktionen:
6
-----
Die Funktion rechneJahr, rechneJahr_ (ab 1970)
und rechneMJahr (bis 1970)
berechnen, wie viele Jahre in die gegebene Zahl passen.
Dabei wird ein Tupel (wieviel Jahre, RestTage) zurueckgegeben.
rechneJahr a
= if a> (-1) then
if (a < yearLengths 1970)
then (1970,a)
else
rechneJahr_ (a-yearLengths(1970)) 1971 1
else
let b = -a in
if (b<=yearLengths 1969) then (1969,a)
else
rechneMJahr_ (b-yearLengths(1969)) 1968 1
rechneMJahr_ a zs ws =
if (a <= yearLengths(zs)) then
(1969-ws,(-a))
else
rechneMJahr_ (a-yearLengths(zs)) (zs-1) (ws+1)
rechneJahr_ a zs ws =
if (a < yearLengths(zs)) then
(1970+ws,a)
else
rechneJahr_ (a-yearLengths(zs)) (zs+1) (ws+1)
-- Analog zu rechneJahr, berechnen die folgenden Funktionen die Anzahl der
-- Monate. Rueckgabe ist wiederum ein Tupel, diesmal (Anzahl Monate, Tage)
rechneMonat a j [] = rechneMonat_ a j 1 (monthLengths j)
rechneMonat_ a j c (x:xs) = if a > (-1) then
if (a < x)
then (c,a+1)
else
rechneMonat_ (a-x) j (c+1) xs
else
let b = (-a)
in
rechneMonatM b j 1 (reverse (x:xs))
rechneMonatM a j c (x:xs) = if (a <= x)
then
(13-c, x-a+1)
else
rechneMonatM (a-x) j (c+1) (xs)
-------------------------------- Nachdem die rechne.. Funktionen die Hauptarbeit leisten, ruft toD
7
-- diese geschickt auf.
toD :: Int -> Date
toD a = let (jahr,b) = rechneJahr a
(monat,t) = rechneMonat b jahr []
in
Date jahr monat t
fromD a
= map toEnum [fromEnum a .. ]
fromDT a b = map toEnum [fromEnum a, fromEnum b .. ]
----------------------------------------- Die Berechung fuers Schaltjahr sind aus der Calendar.hs genau so
-- uebernommen.
leap year
= if year`mod`100 == 0 then year`mod`400 == 0
else year`mod`4
== 0
-- Informationen ueber die Monate im Jahr:
monthLengths year = [31,feb,31,30,31,30,31,31,30,31,30,31]
where feb | leap year = 29
| otherwise = 28
-- Die Funktion yearLengths macht einfach ein fold ueber monthLengths
-- und kann so ganz einfach die Anzahl der Tage zu einem vorgegeben Jahr
-- berechnen.
yearLengths year = foldr (+) 0 (monthLengths year)
----------------------------------------------- weitere huebsche Funktionen:
--- Der Abstand zwischen zwei Datumswerten:
distDays a b = toI b - toI a
---Hier die zwei ultimativen Funktionen: zum Addieren und Subtrahieren
-- beliebiger Datumswerte, es ist auch moeglich, zu einem Datum eine Zahl
-- zu Zahl (z. B. Tage zu addieren/subtr.)
addDate a b =toD ((toI a) + (toI b))
subDate a b = toD (toI a - toI b)
8
--------------------------------------------- wTag liefert zu einem Datum den Wochentag zurueck.
-- wTag :: Date -> Int
wTag a = wTag_ (wTagI a)
wTagI a = 1 + mod ((toI a) + 3 ) 7
wTag_ a |
|
|
|
|
|
|
a
a
a
a
a
a
a
==
==
==
==
==
==
==
7
1
2
3
4
5
6
=
=
=
=
=
=
=
"Sonntag"
"Montag"
"Dienstag"
"Mittwoch"
"Donnerstag"
"Freitag"
"Samstag"
------------------------------------------- Bestimmung der Kalenderwoche
fstDay a
= Date (year a) 1 1
kw a = let fDay
= (fstDay a)
-tmp
= toI fDay + (wTagI fDay)
tmp
= toI a - (wTagI a)
tagiJ = tmp - (toI fDay)
woche = div tagiJ 7
in
if (wTagI fDay) < 5 then woche + 2
else
if (woche == (-1)) then 53
else woche +1
------------------------------------------- Hier ein paar Beispiele, damit man die Funktionen testen kann:
startTag = Date { year = 1970, month = 1, day =1 }
fst69
fst68
fst71
bspTag
mbspTag
fst98
fst95
=
=
=
=
=
=
=
Date
Date
Date
Date
Date
Date
Date
{year
{year
{year
{year
{year
{year
{year
=
=
=
=
=
=
=
1969,
1968,
1971,
1998,
1950,
1998,
1995,
month
month
month
month
month
month
month
=
=
=
=
=
=
=
1,
1,
1,
5,
7,
1,
1,
day
day
day
day
day
day
day
--
6.2 Ostern.hs
--
9
=
=
=
=
=
=
=
1}
1}
1}
16}
20}
1}
1}
module
Ostern (
ostern ) where
import Dates
-- Der Gausche Osteralgorithmus
ostern j = let h = div j 100
l = 4 + h - div h 4
m = 15 + h - div h 4 - div (8*h+13) 25
a = mod j 4
b = mod j 7
c = mod j 19
d = mod (19*c + m) 30
e = mod (2*a+4*b+6*d+l) 7
in
if (d==29 && e ==6) then (Date j 4 19)
else
if (d==28 && e==6 && c>=11) then (Date j 4 18)
else
if (22+d+e<=31) then (Date j 3 (22+d+e))
else
(Date j 4 (d+e-9))
aschermittwoch j = (ostern j - 46)
pfingsten j = (ostern j + 49)
christiHim j = (ostern j + 39)
fronleichnam j = (ostern j + 60)
feierTage j = let a =
b =
c =
d =
e =
in [("
("
("
("
("
aschermittwoch j
ostern j
christiHim j
pfingsten j
fronleichnam j
Aschermittwoch:", a),
Ostern: ", b),
Christi Himmelfahrt: ", c),
Pfingsten: ", d),
Fronleichnam: ", e)]
--
10
Herunterladen