-- Mischa Dieterle, FB 12
-- Philipps-Universität Marburg

-- Einzeiliger Kommentar
{-
Mehrzeiliger
Kommentar
-}

module HsExamples  --Modulnamen: (ohne Namen wird Main angenommen und eine Funktion main erwartet)
where
-- imports
import Prelude hiding (filter,map,foldr1)

--Einfache Variablen
x :: Int
x = 3

--Einfache Funktionen
square :: Int -> Int
square x = x * x

--ohne Typangabe: in ghci ":t square'" eingeben
square' x =  x * x

--Einfache zweistellige Funktionen
plus :: Int -> Int -> Int
plus a b = a + b

--Partielle Applikation / currying
suc :: Int -> Int
suc = plus 1

--Funktionskomposition (bei Funktionen mit einem Argument)
sucsuc :: Int -> Int
sucsuc = suc . suc
 
-- or mit if then else
oR,oR',oR'' :: Bool -> Bool -> Bool  -- normalerweise Operator ||
oR x y = if x then True else y

-- or mit Guards (hinter Guards müssen boolesche Ausdrücke stehen)
oR' x y
  | x           = True
  | (y == True) = True
  | otherwise   = False


-- or mit Pattern Matching
oR'' True  _ = True  -- Argument True wird abgeglichen, Argument _ wird ignoriert
oR'' False b = b     -- Argumentvariable b wird gebunden


--Polymorphie
first :: a -> b -> a
first x y = x


r = (+) 3 4
--Listen
leereListe = []
einerListe :: [Int]
einerListe = 1 : []  -- Infixoperator : konstruiert aus Element und Liste


zweierListe   = 2 : einerListe
zweierListe'  = 2 : (1: [])
zweierListe'' = 2 : 1 : []  -- Infixoperator : ist rechtsassoziativ


dreierListe   = 3 : zweierListe
dreierListe'  = 3 : (2: (1: []))
dreierListe'' = 3 : 2 : 1 : []
dreierListe''' = [3, 2, 1]

-- Listen Kurzschreibweisen
bis10 = [1..10]
unendlicheListe = [1..]
gerade = [2,4..]
bis10' = take 10 unendlicheListe
 
--Pattern Matching mit Listen
or',or'' :: [Bool] -> Bool
or' []        = False
or' (x : xs)
  | (x==True) = True
  | otherwise = or' xs

--Pattern Matching mit Listen und geschachtelten Mustern
or'' []           = False
or'' (True  : _)  = True
or'' (False : xs) = or'' xs  

-- lokale definitionen / Funktionsschachtelung (where und let)
-- (Beispiel übertragen aus "scala by example")
wurzel :: Double -> Double
wurzel x = iterWurzelF 1.0 x
  where
    gutGenugF   s x = abs (s ^^ 2 - x) < 0.001
    besserF     s x = (s + x / s) / 2
    iterWurzelF s x = if gutGenugF s x then s else iterWurzelF (besserF s x) x
-- -> lokale Funktionen global nicht sichtbar

-- Funktionsschachtelung 2:
wurzel' :: Double -> Double
wurzel' x = iterWurzelF 1.0
  where
    iterWurzelF s =
      let
          gutGenug = abs (s ^^ 2 - x) < 0.001
          besser   = (s + x / s) / 2  
      in
        if gutGenug then s else iterWurzelF besser
-- -> Argumente sparen die bereits sichtbar sind

--Funktionen höherer Ordnung (mit Funktionsparameter oder Funktionsergebnis)
-- 2.Version Liste gerader Zahlen zu erzeugen: Filtern
gerade' :: [Int]
gerade' = nimmGeradeF unendlicheListe

--direkte Implementierung von nimmGeradeF
nimmGeradeF :: [Int] -> [Int]
nimmGeradeF [] = []
nimmGeradeF (x:xs) | istGerade x = x : nimmGeradeF xs
                   | otherwise   = nimmGeradeF xs

--Parameterfunktion
istGerade :: Int -> Bool
istGerade x = (x `mod` 2 == 0)

--1. Funktion höherer Ordnung: filter
filter :: (a -> Bool) -> [a] -> [a]  --aus Data.List
filter _pred []    = []
filter pred (x:xs)
  | pred x         = x : filter pred xs
  | otherwise      = filter pred xs

--nimmGeradeF mit Filter
nimmGeradeF' :: [Int] -> [Int]
nimmGeradeF' xs = filter istGerade xs

--Argument kann weggelassen werden (Rückgabe ist Funktion die die Liste noch bekommt)
nimmGeradeF'' :: [Int] -> [Int]
nimmGeradeF'' = filter istGerade

-- 3.Version Liste gerader Zahlen zu erzeugen: Multiplizieren
gerade'' :: [Int]
gerade'' = mal2F unendlicheListe

mal2F :: [Int] -> [Int]
mal2F [] = []
mal2F (x:xs) = (2 * x) : mal2F xs

--2. Funktion höherer Ordnung: map
map :: (a -> b) -> [a] -> [b]
map _ []     = []
map f (x:xs) = f x : map f xs

mal2F' :: [Int] -> [Int]
mal2F' xs = map (*2) xs

--Summen von Integerlisten
mySum :: [Int] -> Int
mySum [x]    = x
mySum (x:xs) = x + mySum xs
mySum []     = error "mySum emptyList"

--3. Funktion höherer Ordnung: foldr1
foldr1         :: (a -> a -> a) -> [a] -> a
foldr1 _ [x]    =  x
foldr1 f (x:xs) =  f x (foldr1 f xs)
foldr1 _ []     =  error "errorEmptyList foldr1"

mySum' :: [Int] -> Int
mySum' = foldr1 (+)

-- Lambda Abstraktionen (anonyme Funktionen)

-- Liste gerader Zahlen ohne Hilfsfunktion
gerade''' :: [Int]
gerade''' = filter (\x        ->  x `mod` 2 == 0) unendlicheListe
             --     parameter ->  Funktionskörper

--Typ Aliase (nur Benennung)
type Zahl = Int

type Zahlenstrom = [Int]

type Strom a = [a]

type Zahlenstrom' = Strom Int

--Typ Definitionen
--Monomorph
data SinglePaar = Paar Int Int | Single Int
--Polymorph
data SinglePaar' a b c = Paar' a b | Single' c

type SinglePaar'' = SinglePaar' Int Int Int

--Rekursiv (geht nicht mit type)
data BinTree a = Leaf a | Node (BinTree a) a (BinTree a)  deriving Show

mkTree :: Int -> Int -> BinTree Int
mkTree 1 n = Leaf n
mkTree i n | (i<1)     = error "tree without levels"
           | otherwise = Node (mkTree (i-1) (n-1)) n (mkTree (i-1) (n+1))

--Pattern Matching Beispiel
inorderTraversal :: BinTree a -> [a]
inorderTraversal (Leaf x)       = [x]
inorderTraversal (Node t1 x t2) = inorderTraversal t1 ++ x : inorderTraversal t2

--Typ Klassen (ähnlich zu Interfaces)
{-
Prelude> :i Eq
class Eq a where
  (==) :: a -> a -> Bool
  (/=) :: a -> a -> Bool
-}
instance Eq a => Eq (BinTree a) where
   (Node _ _ _) == (Leaf _) = False  
   (Leaf _) == (Node _ _ _) = False
   (Leaf a) == (Leaf b) = a == b       -- Prüfe Elemente von Typ a
   (Node a1 x a2) == (Node b1 y b2) 
        = x==y &&           -- Prüfe Elemente von Typ a
          a1==b1 && a2==b2  -- Prüfe rekursiv

--Main

main :: IO()
main = do putStrLn "Level?" -- putStrLn :: String -> IO ()
          level <- getLine --getLine :: IO String, level :: String
          wert  <- putStrLn "Startwert?"
          wert  <- getLine
          let t1 = mkTree (read level) (read wert)
              t2 = mkTree (read level) (read wert)
          putStrLn $ "Aequivalenztest sagt: " ++ ( show $ t1 == t2)
