diff --git a/documents/Programmierparadigmen/Arbeitszeit.md b/documents/Programmierparadigmen/Arbeitszeit.md index 474fcee..dea56e9 100644 --- a/documents/Programmierparadigmen/Arbeitszeit.md +++ b/documents/Programmierparadigmen/Arbeitszeit.md @@ -8,3 +8,4 @@ in dem Erstellen dieses Skripts steckt: |01.02.2014 | 14:45 - 15:30 | Thoma | Haskell angefangen |01.02.2014 | 11:15 - 11:45 | Thoma | Haskell Class Hierachy |01.02.2014 | 16:00 - 17:00 | Thoma | Abschnitt über Rekursion hinzugefügt +|04.02.2014 | 13:00 - 14:00 | Thoma | Viel zu Haskell ergänzt; Funktionen höherer Ordnung beschrieben diff --git a/documents/Programmierparadigmen/Haskell.tex b/documents/Programmierparadigmen/Haskell.tex index 7afbbd3..f311f25 100644 --- a/documents/Programmierparadigmen/Haskell.tex +++ b/documents/Programmierparadigmen/Haskell.tex @@ -1,7 +1,7 @@ \chapter{Haskell} \index{Haskell|(} Haskell ist eine funktionale Programmiersprache, die von Haskell -Brooks Curry entwickelt wurde und 1990 in Version~1.0 veröffentlicht +Brooks Curry entwickelt und 1990 in Version~1.0 veröffentlicht wurde. Wichtige Konzepte sind: @@ -21,24 +21,44 @@ Haskell kann unter \href{http://www.haskell.org/platform/}{\path{www.haskell.org für alle Plattformen heruntergeladen werden. Unter Debian-Systemen ist das Paket \texttt{ghc} bzw. \texttt{haskell-platform} relevant. -\section{Typen} -Siehe \cref{fig:haskell-type-hierarchy}: +\subsection{Hello World} +Speichere folgenden Quelltext als \texttt{hello-world.hs}: +\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=hello-world.hs]{haskell}{scripts/haskell/hello-world.hs} -\begin{figure}[htp] - \centering - \resizebox{0.9\linewidth}{!}{\input{figures/haskell-type-classes.tex}} - \caption{Hierarchie der Haskell Standardklassen} - \label{fig:haskell-type-hierarchy} -\end{figure} +Kompiliere ihn mit \texttt{ghc -o hello hello-world.hs}. Es wird eine +ausführbare Datei erzeugt. \section{Syntax} -\subsection{Klammern} +\subsection{Klammern und Funktionsdeklaration} Haskell verzichtet an vielen Stellen auf Klammern. So werden im Folgenden die Funktionen $f(x) := \frac{\sin x}{x}$ und $g(x) := x \cdot f(x^2)$ definiert: \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/einfaches-beispiel-klammern.hs} +Die Funktionsdeklarationen mit den Typen sind nicht notwendig, da +die Typen aus den benutzten Funktionen abgeleitet werden. + +Zu lesen ist die Deklaration wie folgt: + +\begin{center} +\texttt{[Funktionsname] :: \texttt{[Typendefinitionen]} => \texttt{Signatur}} +\end{center} + +\begin{itemize} + \item[T. Def.] Die Funktion \texttt{f} benutzt als Parameter bzw. Rückgabewert + einen Typen. Diesen Typen nennen wir \texttt{a} und er ist + vom Typ \texttt{Floating}. Auch \texttt{b}, \texttt{wasweisich} + oder etwas ähnliches wäre ok. + \item[Signatur] Die Signatur liest man am einfachsten von hinten: + \begin{itemize} + \item \texttt{f} bildet auf einen Wert vom Typ \texttt{a} ab und + \item \texttt{f} hat genau einen Parameter \texttt{a} + \end{itemize} +\end{itemize} + +\todo[inline]{Gibt es Funktionsdeklarationen, die äquivalent? (bis auf wechsel des namens und der Reihenfolge)} + \subsection{if / else} Das folgende Beispiel definiert den Binomialkoeffizienten (vgl. \cref{bsp:binomialkoeffizient}) @@ -61,23 +81,86 @@ hat einen Speicherverbrauch von $\mathcal{O}(n)$. Durch einen \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/fakultaet-akkumulator.hs} \subsection{Listen} -\todo[inline]{Cons-Operator, Unendliche Listen} +\begin{itemize} + \item \texttt{[]} erzeugt die leere Liste, + \item \texttt{[1,2,3]} erzeugt eine Liste mit den Elementen $1, 2, 3$ + \item \texttt{:} wird \textbf{cons}\xindex{cons} genannt und ist + der Listenkonstruktor. + \item \texttt{head list} gibt den Kopf von \texttt{list} zurück, + \texttt{tail list} den Rest: + \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/list-basic.sh} + \item \texttt{null list} prüft, ob \texttt{list} leer ist. + \item \texttt{length list} gibt die Anzahl der Elemente in \texttt{list} zurück. + \item \texttt{maximum [1,9,1,3]} gibt 9 zurück (analog: \texttt{minimum}). + \item \texttt{last [1,9,1,3]} gibt 3 zurück. + \item \texttt{reverse [1,9,1,3]} gibt \texttt{[3,1,9,1]} zurück. + \item \texttt{elem item list} gibt zurück, ob sich \texttt{item} in \texttt{list} befindet. +\end{itemize} \subsubsection{Beispiel in der interaktiven Konsole} \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/listenoperationen.sh} +\subsubsection{List-Comprehensions}\xindex{List-Comprehension} +List-Comprehensions sind kurzschreibweisen für Listen, die sich an +der Mengenschreibweise in der Mathematik orientieren. So entspricht +die Menge +\begin{align*} + myList &= \Set{1,2,3,4,5,6}\\ + test &= \Set{x \in myList | x > 2} +\end{align*} +in etwa folgendem Haskell-Code: +\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/list-comprehensions.sh} + +\subsection{Strings} +\begin{itemize} + \item Strings sind Listen von Zeichen:\\ + \texttt{tail "ABCDEF"} gibt \texttt{"BCDEF"} zurück. +\end{itemize} + +\section{Typen} +In Haskell werden Typen aus den Operationen geschlossfolgert. Dieses +Schlussfolgern der Typen, die nicht explizit angegeben werden müssen, +nennt man \textbf{Typinferent}\xindex{Typinferenz}. + + +Haskell kennt die Typen aus \cref{fig:haskell-type-hierarchy}. + +Ein paar Beispiele zur Typinferenz: +\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/typinferenz.sh} + +\begin{figure}[htp] + \centering + \resizebox{0.9\linewidth}{!}{\input{figures/haskell-type-classes.tex}} + \caption{Hierarchie der Haskell Standardklassen} + \label{fig:haskell-type-hierarchy} +\end{figure} + \section{Beispiele} -\subsection{Hello World} -Speichere folgenden Quelltext als \texttt{hello-world.hs}: -\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=hello-world.hs]{haskell}{scripts/haskell/hello-world.hs} +\subsection{Quicksort} +\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=qsort.hs]{haskell}{scripts/haskell/qsort.hs} -Kompiliere ihn mit \texttt{ghc -o hello hello-world.hs}. Es wird eine -ausführbare Datei erzeugt. +\begin{itemize} + \item Die leere Liste ergibt sortiert die leere Liste. + \item Wähle das erste Element \texttt{p} als Pivotelement und + teile die restliche Liste \texttt{ps} in kleinere und + gleiche sowie in größere Elemente mit \texttt{filter} auf. + Konkateniere diese beiden Listen mit \texttt{++}. +\end{itemize} -\subsection{Fibonacci} +Durch das Ausnutzen von Unterversorgung\xindex{Unterversorgung} lässt +sich das ganze sogar noch kürzer schreiben: + +\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=qsort.hs]{haskell}{scripts/haskell/qsort-unterversorg.hs} + +\subsection{Fibonacci}\xindex{Fibonacci} \inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=fibonacci.hs]{haskell}{scripts/haskell/fibonacci.hs} +\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=fibonacci-akk.hs]{haskell}{scripts/haskell/fibonacci-akk.hs} +\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=fibonacci-zip.hs]{haskell}{scripts/haskell/fibonacci-zip.hs} +\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=fibonacci-pattern-matching.hs]{haskell}{scripts/haskell/fibonacci-pattern-matching.hs} \subsection{Quicksort} +\subsection{Funktionen höherer Ordnung} + \section{Weitere Informationen} \begin{itemize} diff --git a/documents/Programmierparadigmen/Programmierparadigmen.pdf b/documents/Programmierparadigmen/Programmierparadigmen.pdf index 7aea649..d2bbfac 100644 Binary files a/documents/Programmierparadigmen/Programmierparadigmen.pdf and b/documents/Programmierparadigmen/Programmierparadigmen.pdf differ diff --git a/documents/Programmierparadigmen/Programmiertechniken.tex b/documents/Programmierparadigmen/Programmiertechniken.tex index f939ec9..e9807f7 100644 --- a/documents/Programmierparadigmen/Programmiertechniken.tex +++ b/documents/Programmierparadigmen/Programmiertechniken.tex @@ -18,9 +18,10 @@ fib(n-1) + fib(n-2) &\text{sonst} \end{cases} \end{align*} + Erzeugt die Zahlen $0, 1, 1, 2, 3, 5, 8, 13, \dots$ \item Fakultät:\xindex{Fakultät} \begin{align*} - !: \mdn_0 &\rightarrow \mdn_0\\ + ! &: \mdn_0 \rightarrow \mdn_0\\ n! &= \begin{cases} 1 &\text{falls } n \leq 1\\ n\cdot (n-1)! &\text{sonst} @@ -28,7 +29,7 @@ \end{align*} \item \label{bsp:binomialkoeffizient} Binomialkoeffizient:\xindex{Binomialkoeffizient} \begin{align*} - \binom{\cdot}{\cdot}: \mdn_0 \times \mdn_0 &\rightarrow \mdn_0\\ + \binom{\cdot}{\cdot} &: \mdn_0 \times \mdn_0 \rightarrow \mdn_0\\ \binom{n}{k} &= \begin{cases} 1 &\text{falls } k=0 \lor k = n\\ \binom{n-1}{k-1}+\binom{n-1}{k} &\text{sonst} @@ -79,20 +80,54 @@ Mit Hilfe der Formel von Moivre-Binet folgt: Dabei ist der Speicherbedarf $\mathcal{O}(n)$. Dieser kann durch das Benutzen eines Akkumulators signifikant reduziert werden.\todo{TODO} -\begin{definition}[linear rekursive Funktion]\xindex{Funktion!linear rekursive} +\begin{definition}[linear rekursive Funktion]\xindex{Funktion!linear rekursive}% Eine Funktion heißt linear rekursiv, wenn in jedem Definitionszweig der Funktion höchstens ein rekursiver Aufruf vorkommt. \end{definition} -\begin{definition}[endrekursive Funktion]\xindex{Funktion!endrekursive}\xindex{tail recursive} +\begin{definition}[endrekursive Funktion]\xindex{Funktion!endrekursive}\xindex{tail recursive}% Eine Funktion heißt endrekursiv, wenn in jedem Definitionszweig der Rekursive aufruf am Ende des Ausdrucks steht. Der rekursive Aufruf darf also insbesondere nicht in einen anderen Ausdruck eingebettet sein. \end{definition} -\todo[inline]{Beispiele für linear rekusrive, endrekursive Funktionen (alle Kombinationen+gegenbeispiele} +Auf Englisch heißen endrekursive Funktionen \textit{tail recursive}. -\index{Rekursion|(} +\begin{beispiel}[Linear- und endrekursive Funktionen] + \begin{bspenum} + \item \texttt{fak n = if (n==0) then 1 else (n * fak (n-1))}\\ + ist eine linear rekursive Funkion, aber nicht endrekursiv, + da nach der Rückgabe von \texttt{fak (n-1)} noch die Multiplikation + ausgewertet werden muss. + \item \texttt{fakAcc n acc = if (n==0) then acc else fakAcc (n-1) (n*acc)}\\ + ist eine endrekursive Funktion. + \item \texttt{fib n = n <= 1 ? n : fib(n-1) + fib (n-2)}\\ + ist weder linear- noch endrekursiv. + \end{bspenum} +\end{beispiel} + +\index{Rekursion|)} \section{Backtracking} +\index{Backtracking|(} +\index{Backtracking|)} + +\section{Funktionen höherer Ordnung} +Funktionen höherer Ordnung sind Funktionen, die auf Funktionen arbeiten. +Bekannte Beispiele sind: +\begin{itemize} + \item \texttt{map(function, list)}\xindex{map}\\ + \texttt{map} wendet \texttt{function} auf jedes einzelne + Element aus \texttt{list} an. + \item \texttt{filter(function, list)}\xindex{filter}\\ + \texttt{filter} gibt eine Liste aus Elementen zurück, für + die \texttt{function} mit \texttt{true} evaluiert. + \item \texttt{reduce(function, list)}\xindex{reduce}\\ + \texttt{function} ist für zwei Elemente aus \texttt{list} + definiert und gibt ein Element des gleichen Typs zurück. + Nun steckt \texttt{reduce} zuerst zwei Elemente aus \texttt{list} + in \texttt{function}, merkt sich dann das Ergebnis und nimmt + so lange weitere Elemente aus \texttt{list}, bis jedes + Element genommen wurde. +\end{itemize} diff --git a/documents/Programmierparadigmen/scripts/haskell/binomialkoeffizient.hs b/documents/Programmierparadigmen/scripts/haskell/binomialkoeffizient.hs index 5a4fd2b..f2a5217 100644 --- a/documents/Programmierparadigmen/scripts/haskell/binomialkoeffizient.hs +++ b/documents/Programmierparadigmen/scripts/haskell/binomialkoeffizient.hs @@ -1,3 +1,4 @@ +binom :: (Eq a, Num a, Num a1) => a -> a -> a1 binom n k = if (k==0) || (k==n) then 1 diff --git a/documents/Programmierparadigmen/scripts/haskell/einfaches-beispiel-klammern.hs b/documents/Programmierparadigmen/scripts/haskell/einfaches-beispiel-klammern.hs index 03dfeb5..9e597ad 100644 --- a/documents/Programmierparadigmen/scripts/haskell/einfaches-beispiel-klammern.hs +++ b/documents/Programmierparadigmen/scripts/haskell/einfaches-beispiel-klammern.hs @@ -1,2 +1,5 @@ +f :: Floating a => a -> a f x = sin x / x + +g :: Floating a => a -> a g x = x * (f (x*x)) diff --git a/documents/Programmierparadigmen/scripts/haskell/fakultaet-akkumulator.hs b/documents/Programmierparadigmen/scripts/haskell/fakultaet-akkumulator.hs index cd4f558..4302f26 100644 --- a/documents/Programmierparadigmen/scripts/haskell/fakultaet-akkumulator.hs +++ b/documents/Programmierparadigmen/scripts/haskell/fakultaet-akkumulator.hs @@ -1,4 +1,7 @@ +fakAcc :: (Eq a, Num a) => a -> a -> a fakAcc n acc = if (n==0) then acc else fakAcc (n-1) (n*acc) + +fak :: (Eq a, Num a) => a -> a fak n = fakAcc n 1 diff --git a/documents/Programmierparadigmen/scripts/haskell/fakultaet.hs b/documents/Programmierparadigmen/scripts/haskell/fakultaet.hs index 9e3fad9..b9e9911 100644 --- a/documents/Programmierparadigmen/scripts/haskell/fakultaet.hs +++ b/documents/Programmierparadigmen/scripts/haskell/fakultaet.hs @@ -1 +1,2 @@ +fak :: (Eq a, Num a) => a -> a fak n = if (n==0) then 1 else n * fak (n-1) diff --git a/documents/Programmierparadigmen/scripts/haskell/fibonacci-akk.hs b/documents/Programmierparadigmen/scripts/haskell/fibonacci-akk.hs new file mode 100644 index 0000000..41d5d29 --- /dev/null +++ b/documents/Programmierparadigmen/scripts/haskell/fibonacci-akk.hs @@ -0,0 +1,5 @@ +fibAkk n n1 n2 + | (n == 0) = n1 + | (n == 1) = n2 + | otherwise = fibAkk (n - 1) n2 (n1 + n2) +fib n = fibAkk n 0 1 diff --git a/documents/Programmierparadigmen/scripts/haskell/fibonacci-pattern-matching.hs b/documents/Programmierparadigmen/scripts/haskell/fibonacci-pattern-matching.hs new file mode 100644 index 0000000..2782c8e --- /dev/null +++ b/documents/Programmierparadigmen/scripts/haskell/fibonacci-pattern-matching.hs @@ -0,0 +1,3 @@ +fib 0 = 0 +fib 1 = 1 +fib n = fib (n - 1) + fib (n - 2) diff --git a/documents/Programmierparadigmen/scripts/haskell/fibonacci-zip.hs b/documents/Programmierparadigmen/scripts/haskell/fibonacci-zip.hs new file mode 100644 index 0000000..25efdce --- /dev/null +++ b/documents/Programmierparadigmen/scripts/haskell/fibonacci-zip.hs @@ -0,0 +1 @@ +fib = 0 : 1 : zipWith (+) fibs (tail fibs) diff --git a/documents/Programmierparadigmen/scripts/haskell/fibonacci.hs b/documents/Programmierparadigmen/scripts/haskell/fibonacci.hs index ac462a9..d8f9405 100644 --- a/documents/Programmierparadigmen/scripts/haskell/fibonacci.hs +++ b/documents/Programmierparadigmen/scripts/haskell/fibonacci.hs @@ -1 +1,4 @@ -fibs = 0 : 1 : zipWith (+) fibs (tail fibs) +fib n + | (n == 0) = 0 + | (n == 1) = 1 + | otherwise = fib (n - 1) + fib (n - 2) \ No newline at end of file diff --git a/documents/Programmierparadigmen/scripts/haskell/list-basic.sh b/documents/Programmierparadigmen/scripts/haskell/list-basic.sh new file mode 100644 index 0000000..dfa68c1 --- /dev/null +++ b/documents/Programmierparadigmen/scripts/haskell/list-basic.sh @@ -0,0 +1,12 @@ +Prelude> head [] +*** Exception: Prelude.head: empty list +Prelude> tail [] +*** Exception: Prelude.tail: empty list +Prelude> tail [1] +[] +Prelude> head [1] +1 +Prelude> null [] +True +Prelude> null [[]] +False diff --git a/documents/Programmierparadigmen/scripts/haskell/list-comprehensions.sh b/documents/Programmierparadigmen/scripts/haskell/list-comprehensions.sh new file mode 100644 index 0000000..93e45fd --- /dev/null +++ b/documents/Programmierparadigmen/scripts/haskell/list-comprehensions.sh @@ -0,0 +1,4 @@ +Prelude> let mylist = [1,2,3,4,5,6] +Prelude> let test = [x | x <- mylist, x>2] +Prelude> test +[3,4,5,6] diff --git a/documents/Programmierparadigmen/scripts/haskell/qsort-unterversorg.hs b/documents/Programmierparadigmen/scripts/haskell/qsort-unterversorg.hs new file mode 100644 index 0000000..83e3e42 --- /dev/null +++ b/documents/Programmierparadigmen/scripts/haskell/qsort-unterversorg.hs @@ -0,0 +1,3 @@ +qsort [] = [] +qsort (p:ps) = (qsort (filter (<=p) ps)) + ++ p:(qsort (filter (> p) ps)) diff --git a/documents/Programmierparadigmen/scripts/haskell/qsort.hs b/documents/Programmierparadigmen/scripts/haskell/qsort.hs new file mode 100644 index 0000000..80eebc9 --- /dev/null +++ b/documents/Programmierparadigmen/scripts/haskell/qsort.hs @@ -0,0 +1,3 @@ +qsort [] = [] +qsort (p:ps) = (qsort (filter (\x -> x<=p) ps)) + ++ p:(qsort (filter (\x -> x> p) ps)) diff --git a/documents/Programmierparadigmen/scripts/haskell/typinferenz.sh b/documents/Programmierparadigmen/scripts/haskell/typinferenz.sh new file mode 100644 index 0000000..663b389 --- /dev/null +++ b/documents/Programmierparadigmen/scripts/haskell/typinferenz.sh @@ -0,0 +1,35 @@ +Prelude> let x = \x -> x*x +Prelude> :t x +x :: Integer -> Integer +Prelude> x(2) +4 +Prelude> x(2.2) +:6:3: + No instance for (Fractional Integer) + arising from the literal `2.2' + Possible fix: add an instance declaration for + (Fractional Integer) + In the first argument of `x', namely `(2.2)' + In the expression: x (2.2) + In an equation for `it': it = x (2.2) + + +Prelude> let mult = \x y->x*y +Prelude> mult(2,5) +:9:5: + Couldn't match expected type `Integer' with + actual type `(t0, t1)' + In the first argument of `mult', namely `(2, 5)' + In the expression: mult (2, 5) + In an equation for `it': it = mult (2, 5) +Prelude> mult 2 5 +10 +Prelude> :t mult +mult :: Integer -> Integer -> Integer + +Prelude> let concat = \x y -> x ++ y +Prelude> concat [1,2,3] [3,2,1] +[1,2,3,3,2,1] +Prelude> :t concat +concat :: [a] -> [a] -> [a] +