home Grundlagen der Funktionalen Programmierung: Selbstdefinierte Datentypen Prof. Dr. Uwe Schmidt FH Wedel

Selbstdefinierte Datentypen

weiter

weiter

Typdeklarationen

Aliasnamen
für eigene zusammengesetzte Typen
weiter
Beispiele
type String   = [Char]
type Position = (Int, Int)
type Board    = [Position]
weiter
merke
type-Deklarationen dürfen keine Rekursion enthalten.
schlecht
type Tree = (Int, [Tree])  -- Fehler
gut
Lesbarkeit von Programmen kann verbessert werden
gut
Typdeklarationen dürfen parametrisiert sein
 
type Parser a  = String -> (a, String)
type Assoc k v = [(k, v)]
merke
Alle Typnamen können durch die zugehörige rechte Seite ersetzt werden,
ohne dass die Bedeutung eines Programms verändert wird.

weiter

Neue selbstdefinierte Datentypen

data-Deklarationen
für eigene Wertebereiche
Beispiele
data Color = Red | Green | Blue
 
data Move  = Left | Right | Up | Down
weiter
merke
Color ist ein Wertebereich mit 3 Werten
Move mit 4 Werten
merke
Red, Green, Blue repräsentieren die 3 Werte von Color
Left, Right, Up, Down die 4 Werte von Move
 
Red   :: Color
Green :: Color
Blue  :: Color
 
Left  :: Move
Right :: Move
Up    :: Move
Down  :: Move
merke
Red, Green, Blue und Left, Right, Up, Down heißen Konstruktoren
merke
| wird als oder gelesen
weiter
Definition
data-Definitionen, die ein | enthalten, heißen Summen-Datentypen
weiter
Wahrheitswerte
sind durch eine data-Definition vordefiniert
 
data Bool = False | True
weiter
gut
Konstruktoren dürfen in Mustern verwendet werden
Beispiel
move :: Move -> Position -> Position
 
move Left  (x, y) = (x - 1, y)
move Right (x, y) = (x + 1, y)
move Up    (x, y) = (x, y - 1)
move Down  (x, y) = (x, y + 1)
 
moves :: [Move] -> Position -> Position
 
moves []       p = p
moves (m : ms) p = moves ms (move m p)
merke
[] und : sind die einzigen Konstruktoren für Listen
weiter
Vereinfachung
moves besitzt das Rekursionsschema von foldl
 
  moves ms p0 = foldl (λ p m -> move m p) p0 ms
={ Definition von flip }
  moves ms p0 = foldl (flip move) p0 ms
={ Definition von flip }
  moves = flip (foldl (flip move))
weiter
 
moves :: [Move] -> Position -> Position
moves = flip (foldl (flip move))
 
-- vordefiniert:
 
flip f = λ x y -> f y x
weiter
Konstruktoren
mit Argumenten
weiter
Beispiel
data Shape = Circle Float
           | Rectangle Float Float
weiter
Werte von Shape
Circle 1.0        :: Shape
Rectangle 2.0 3.0 :: Shape
 
Circle    :: Float -> Shape
Rectangle :: Float -> Float -> Shape
weiter
merke
Shape ist die Menge aller mit Circle und aller mit Rectangle konstruierbaren Werte
weiter
merke
Konstruktoren können 0, 1, 2, ..., n, ... Argumente besitzen
merke
Die Argumente dürfen unterschiedliche Typen besitzen
weiter
Funktionen
mit Shape-Werten
 
square :: Float -> Shape
square n = Rect n n
 
area :: Shape -> Float
area (Circle r)      = pi * r^2
area (Rectangle w h) = w * h
weiter
Polymorphe Datentypen
data-Definitionen
können (wie Typdeklarationen) Typparameter besitzen
weiter
Beispiel
data Pair a b = Pair a b
 
data Maybe a  = Nothing
              | Just a
weiter
merke
Pair a b ist isomorph zu (a, b)
Paare und n-Tupel sind nur eine bequeme, an die Matematik angelehnte Notation.
merke
Pair a b ist kein Summen-Datentyp, da es nur einen Konstruktor gibt
?
Wie viele Werte hat Maybe a, wenn a n Werte enthält?
gut
Maybe ist vordefiniert
weiter
Anwendung
Aus partiellen Funktionen totale Funktionen machen
-- partiell definiert
 
div :: Integral a => a -> a -> a
div x 0 = error "division by zero"
div x y = ...
 
-- total definiert
 
safeDiv :: Integral a => a -> a -> Maybe a
safeDiv x 0 = Nothing
safeDiv x y = Just (x `div` y)
weiter
merke
Der Nutzer von safeDiv wird gezwungen, eine Fehlerüberprüfung zu machen,
bevor mit dem Quotienten weiter gerechnet wird.
Definition
data-Typen mit Typparametern heißen Typkonstruktoren
 
type MaybeInt = Maybe Int
weiter
Verarbeiten
von Maybe-Werten
 
maybe :: b -> (a -> b) -> Maybe a -> b  -- vordefiniert
 
maybe c f Nothing  = c
maybe c f (Just x) = f x
 
-- Spezialisierung
 
fromMaybe :: a -> Maybe a -> a         -- aus Data.Maybe
fromMaybe c = maybe c id
merke
maybe ist das fold für Maybe-Werte
weiter
 
mapMaybe :: (a -> b) -> Maybe a -> Maybe b
 
mapMaybe f Nothing  = Nothing
mapMaybe f (Just x) = Just (f x)
 
-- oder
 
mapMaybe f = maybe Nothing (Just . f)
merke
mapMaybe ist das map für Maybe-Werte

weiter

Selbstdefinierte rekursive Datentypen

Rekursion
In data-Definitionen darf der Typname in der rechten Seite verwendet werden
rekursive Datenstrukturen
weiter
Beispiel
Die natürlichen Zahlen als Peano-Zahlen
 
data Nat = Zero
         | Succ Nat
 
-- Werte
 
Zero
Succ Zero
Succ (Succ Zero)
Succ (Succ (Succ Zero))
Succ (Succ (Succ (Succ Zero)))
Succ (Succ (Succ (Succ (Succ Zero))))
...
 
-- Addition
 
add :: Nat -> Nat -> Nat
add Zero     m = m
add (Succ n) m = Succ (add n m)
Konversion
von Zahlen in Peano-Darstellung
 
intToNat :: Int -> Nat
intToNat 0 = Zero
intToNat n = Succ (intToNat (n-1))
 
natToInt :: Nat -> Int
natToInt Zero     = 0
natToInt (Succ n) = 1 + natToInt n
merke
intToNat und natToInt bilden einen Isomorphismus zwischen Int und Nat
weiter
merke
Datenstruktur sieht sehr nach einer Liste aus!
weiter
Selbst definierte Listen
data List a = Nil
            | Cons a (List a)
 
-- [] = Nil
-- :  = Cons
weiter
gut
Struktur der Algorithmen ableitbar aus der Struktur der Datentypen
Beispiele
sum :: Num a => List a -> a
 
sum Nil         = 0
sum (Cons x xs) = x + sum xs
 
foldr :: (a -> b -> b) -> b -> List a -> b
 
foldr op c Nil         = c
foldr op c (Cons x xs) = x `op` foldr op c xs
Listen
Eine rekursive Komponente => Verarbeitung mit linearer Rekursion
weiter
Allgemeines Verarbeitungsschema
für beliebige Datentypen
.1
Für jeden Konstruktor gibt es eine Verarbeitungsregel
.2
Alle Komponenten eines Konstruktors werden verarbeitet und zu einem Resultat zusammen gesetzt
.3
Bei einer Rekursion im Datentyp wird die Komponente mit einem rekursiven Aufruf verarbeitet
weiter
Bäume
mehrere Rekursionen in der Datenstruktur
Beispiel
Binäre Bäume mit Information an den inneren Knoten
 
data Tree a = Null
            | Node (Tree a) a (Tree a)
 
type IntTree = Tree Int
weiter
Suchen
in einem Baum
 
occurs :: Eq a => a -> Tree a -> Bool
 
occurs x Null         = False
occurs x (Node l y r) = x == y
                        ||
                        occurs x l
                        ||
                        occurs x r
weiter
Transformation
in eine Liste
 
flatten :: Tree a -> [a]
 
flatten Null         = []
flatten (Node l y r) = flatten l
                       ++
                       [y]
                       ++
                       flatten r
weiter
merke
Diese Datenstruktur wird häufig als binärer Suchbaum eingesetzt
weiter
Konsistenzbedingung
für binäre Suchbäume
 
Für alle Teilbäume gilt:

Alle Werte im linken Teilbaum sind kleiner als der Wert an der Wurzel
Alle Werte im rechten Teilbaum sind größer als der Wert an der Wurzel

weiter
Definition
Solche Konsistenzbedingungen heißen Datenstruktur-Invariante
weiter
Invariante
für einen binären Suchbaum
 
invSearchTree :: Ord a => Tree a -> Bool
 
invSearchTree Null
  = True
 
invSearchTree (Node l x r)
  = lt x l
    &&
    gt x r
    &&
    invSearchTree l
    &&
    invSearchTree r
  where
    lt x Null
      = True
    lt x (Node l y r)
      = y < x && lt x l && lt x r
 
    gt x Null
      = True
    gt x (Node l y r)
      = y > x && gt x l && gt x r
weiter
Suchen
in einem binären Suchbaum
 
member :: Ord a => a -> Tree a -> Bool
 
member x Null     = False
member x (Node l y r)
  | x < y         = member x l
  | x > y         = member x r
  | otherwise     = True
gut
Suchen im Mittel in logarithmischer Zeit in Abhängigkeit von der Anzahl der Elemente im Baum
weiter
Varianten
von Bäumen
weiter
Binäre Bäume
mit Information an den Blättern
 
data Tree2 a = Leaf a
             | Fork (Tree2 a) (Tree2 a)
weiter
Bäume
mit beliebig vielen Teilbäumen
Rhododendron-Bäume (rose trees)
 
data Tree3 a = Node a [Tree3 a]
weiter

weiter

Beispiele zum Kapitel

   1module TypeDecl
   2where
   3
   4import Prelude hiding ( Left
   5                      , Right
   6                      )
   7
   8-- ----------------------------------------
   9
  10type Pos = (Int, Int)
  11
  12data Color
  13  = Red | Green | Blue
  14  deriving (Show)
  15
  16data Move
  17  = Left | Right | Up | Down
  18  deriving (Show)
  19
  20move :: Move -> Pos -> Pos
  21move Left  (x, y) = (x - 1, y    )
  22move Right (x, y) = (x + 1, y    )
  23move Up    (x, y) = (x,     y - 1)
  24move Down  (x, y) = (x,     y + 1)
  25
  26moves :: [Move] -> Pos -> Pos
  27moves ms p0 = foldl (\ p m -> move m p) p0 ms
  28
  29moves' :: [Move] -> Pos -> Pos
  30moves' = flip (foldl (flip move))
  31
  32moves'' :: [Move] -> Pos -> Pos
  33moves'' ms = foldl (>>>) id fs
  34  where
  35    fs    = map move ms
  36    (>>>) = flip (.)
  37
  38ms :: [Move]
  39ms = [Down, Left, Down, Right, Up]
  40
  41ms0, ms0', ms0'' :: Pos
  42ms0   = moves   ms (0,0)
  43ms0'  = moves'  ms (0,0)
  44ms0'' = moves'' ms (0,0)
  45
  46-- ----------------------------------------
  47
  48data Shape
  49    = Circle Float
  50    | Rect Float Float
  51      deriving (Show)
  52
  53square   :: Float -> Shape
  54square n = Rect n n
  55
  56area            :: Shape -> Float
  57area (Circle r) = pi * r * r
  58area (Rect w h) = w * h
  59
  60-- ----------------------------------------
  61
  62safeDiv :: Int -> Int -> Maybe Int
  63safeDiv _ 0 = Nothing
  64safeDiv x y = Just (x `div` y)
  65
  66-- ----------------------------------------
  67
  68data Nat
  69    = Zero
  70    | Succ Nat
  71      deriving (Show)
  72
  73intToNat :: Int -> Nat
  74intToNat 0 = Zero
  75intToNat n = Succ (intToNat (n-1))
  76
  77natToInt :: Nat -> Int
  78natToInt Zero = 0
  79natToInt (Succ n) = 1 + natToInt n
  80
  81add :: Nat -> Nat -> Nat
  82add Zero     n = n
  83add (Succ m) n = Succ (add m n)
  84
  85sub :: Nat -> Nat -> Nat
  86sub n        Zero     = n
  87sub (Succ m) (Succ n) = sub m n
  88sub _ _               = error "negative numbers not allowed"
  89
  90mul :: Nat -> Nat -> Nat
  91mul Zero _n    = Zero
  92mul (Succ m) n = (m `mul` n) `add` n
  93
  94-- ----------------------------------------
  95
  96data List a
  97    = Nil
  98    | Cons a (List a)
  99      deriving (Show)
 100
 101-- ----------------------------------------
 102
 103data IntTree
 104    = Leaf
 105    | Node IntTree Int IntTree
 106      deriving (Show)
 107
 108t :: IntTree
 109t = Node
 110      (Node
 111        (Node Leaf 1 Leaf)
 112        3
 113        (Node Leaf 4 Leaf)
 114      )
 115      5
 116      (Node
 117        (Node Leaf 6 Leaf)
 118        7
 119        (Node Leaf 9 Leaf)
 120      )
 121
 122occurs :: Int -> IntTree -> Bool
 123occurs _m Leaf
 124    = False
 125
 126occurs m (Node l n r)
 127    = m == n
 128      ||
 129      occurs m l
 130      ||
 131      occurs m r
 132
 133flatten :: IntTree -> [Int]
 134flatten Leaf
 135    = []
 136flatten (Node l n r)
 137    = flatten l ++ [n] ++ flatten r
 138
 139-- ----------------------------------------
 140
 141data Tree1 a
 142    = Leaf1 a
 143    | Node1 (Tree1 a) (Tree1 a)
 144      deriving (Show)
 145
 146data Tree2 a
 147    = Leaf2
 148    | Node2 (Tree2 a) a (Tree2 a)
 149      deriving (Show)
 150
 151data Tree3 a b
 152    = Leaf3 a
 153    | Node3 (Tree3 a b) b (Tree3 a b)
 154      deriving (Show)
 155
 156data Tree4 a
 157    = Node4 a [Tree4 a]
 158      deriving (Show)
 159
 160-- ----------------------------------------

weiter

Die Quelle

TypeDecl.hs

Letzte Änderung: 15.12.2020
© Prof. Dr. Uwe Schmidt
Prof. Dr. Uwe Schmidt FH Wedel