📚 The CoCalc Library - books, templates and other resources
License: OTHER
%!TEX root = Programmierparadigmen.tex1\chapter{Haskell}2\index{Haskell|(}3Haskell ist eine funktionale Programmiersprache, die 1990 in Version~1.0 veröffentlicht4wurde. Namensgeber ist Haskell Brooks Curry, der die mathematischen Grundlagen der funktionalen Programmierung entwickelte.56Wichtige Konzepte sind:7\begin{enumerate}8\item Funktionen höherer Ordnung9\item anonyme Funktionen (sog. Lambda-Funktionen)10\item Pattern Matching11\item Unterversorgung12\item Typinferenz13\end{enumerate}1415Haskell kann mit \enquote{Glasgow Haskell Compiler} mittels16\texttt{ghci} interpretiert und mittels1718\section{Erste Schritte}19Haskell kann unter \href{http://www.haskell.org/platform/}{\path{www.haskell.org/platform/}}20für alle Plattformen heruntergeladen werden. Unter Debian-Systemen21ist das Paket \texttt{ghc} bzw. \texttt{haskell-platform} relevant.2223\subsection{Hello World}24Speichere folgenden Quelltext als \texttt{hello-world.hs}:25\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=hello-world.hs]{haskell}{scripts/haskell/hello-world.hs}2627Kompiliere ihn mit \texttt{ghc -o hello hello-world.hs}. Es wird eine28ausführbare Datei erzeugt.2930Alternativ kann es direkt mit \texttt{runghc hello-world.hs} ausgeführt werden.3132\section{Syntax}33\subsection{Kommentare}\xindex{Kommentare!Haskell}34In Haskell werden kommentare durch \verb+--+ begonnen.3536\subsection{Klammern und Funktionsdeklaration}37Haskell verzichtet an vielen Stellen auf Klammern. So werden im38Folgenden die Funktionen $f(x) := \frac{\sin x}{x}$ und $g(x) := x \cdot f(x^2)$39definiert:4041\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/einfaches-beispiel-klammern.hs}4243Die Funktionsdeklarationen mit den Typen sind nicht notwendig, da44die Typen aus den benutzten Funktionen abgeleitet werden.4546Zu lesen ist die Deklaration wie folgt:4748\begin{center}49\texttt{[Funktionsname] :: \texttt{[Typendefinitionen]} => \texttt{Signatur}}50\end{center}5152\begin{itemize}53\item[T. Def.] Die Funktion \texttt{f} benutzt als Parameter bzw. Rückgabewert54einen Typen. Diesen Typen nennen wir \texttt{a} und er ist55vom Typ \texttt{Floating}. Auch \texttt{b}, \texttt{wasweisich}56oder etwas ähnliches wäre ok.57\item[Signatur] Die Signatur liest man am einfachsten von hinten:58\begin{itemize}59\item \texttt{f} bildet auf einen Wert vom Typ \texttt{a} ab und60\item \texttt{f} hat genau einen Parameter \texttt{a}61\end{itemize}62\end{itemize}6364\todo[inline]{Gibt es Funktionsdeklarationen, die bis auf Wechsel des Namens und der Reihenfolge äquivalent sind?}6566\subsection{if / else}\xindex{Haskell!if@\texttt{if}}%67Das folgende Beispiel definiert den Binomialkoeffizienten (vgl. \cref{bsp:binomialkoeffizient}):6869\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/binomialkoeffizient.hs}7071Das könnte man auch mit sog. Guards machen:\xindex{Guard}7273\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/binomialkoeffizient-guard.hs}7475\subsection{Rekursion}76Die Fakultätsfunktion wurde wie folgt implementiert:77\[fak(n) := \begin{cases}781 &\text{falls } n=0\\79n \cdot fak(n) &\text{sonst}80\end{cases}\]81\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/fakultaet.hs}8283Diese Implementierung benötigt $\mathcal{O}(n)$ rekursive Aufrufe und84hat einen Speicherverbrauch von $\mathcal{O}(n)$. Durch einen85\textbf{Akkumulator}\xindex{Akkumulator} kann dies verhindert werden:86\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/fakultaet-akkumulator.hs}8788\subsection{Listen}89Listen sind in Haskell 0-indiziert, d.~h. das erste Element jeder Liste hat90den Index 0.9192\begin{itemize}93\item \texttt{[]} erzeugt die leere Liste,94\item \texttt{[1,2,3]} erzeugt eine Liste mit den Elementen $1, 2, 3$95\item \texttt{:}\xindex{: (Haskell)} wird \textbf{cons}\xindex{cons} genannt und ist96der Listenkonstruktor.97\item \texttt{list !! i}\xindex{"!"! (Haskell)} gibt das $i$-te Element von \texttt{list} zurück.98\item \texttt{head list} gibt den Kopf von \texttt{list} zurück,99\texttt{tail list} den Rest:100\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/list-basic.sh}101\item \texttt{last [1,9,1,3]} gibt 3 zurück.102\item \texttt{length list} gibt die Anzahl der Elemente in \texttt{list} zurück.103\item \texttt{maximum [1,9,1,3]} gibt 9 zurück (analog: \texttt{minimum}).104\item \texttt{null list} prüft, ob \texttt{list} leer ist.105\item \texttt{take 3 [1,2,3,4,5]} gibt \texttt{[1,2,3]} zurück.106\item \texttt{drop 3 [1,2,3,4,5]} gibt \texttt{[4,5]} zurück.107\item \texttt{reverse [1,9,1,3]} gibt \texttt{[3,1,9,1]} zurück.108\item \texttt{elem item list} gibt zurück, ob sich \texttt{item} in \texttt{list} befindet.109\end{itemize}110111\subsubsection{Beispiel in der interaktiven Konsole}112\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/listenoperationen.sh}113114\subsubsection{List-Comprehensions}\xindex{List-Comprehension}%115List-Comprehensions sind kurzschreibweisen für Listen, die sich an116der Mengenschreibweise in der Mathematik orientieren. So entspricht117die Menge118\begin{align*}119myList &= \Set{1,2,3,4,5,6}\\120test &= \Set{x \in myList | x > 2}121\end{align*}122in etwa folgendem Haskell-Code:123\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/list-comprehensions.sh}124125\begin{beispiel}[List-Comprehension]126Das folgende Beispiel zeigt, wie man mit List-Comprehensions die unendliche127Liste aller pythagoreischen Tripels erstellen kann:128129\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/pythagorean-triples.hs}130\end{beispiel}131132\begin{beispiel}[List-Comprehension]133Das folgende Beispiel zeigt, wie man die unendlich große Menge134135\[\Set{p^i | p \text{ Primzahl}, 1 \leq i \leq n}\]136137erstellt:138139\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/primepowers.hs}140141Dabei ist die Reihenfolge wichtig. Würde man zuerst die \verb+i+ und dann142die Primzahlen notieren, würde Haskell versuchen erst alle Primzahlen durch143zu gehen. Da dies eine unendliche Liste ist, würde also niemals die zweite144Potenz einer Primzahl auftauchen.145\end{beispiel}146147\subsection{Strings}148\begin{itemize}149\item Strings sind Listen von Zeichen:\\150\texttt{tail "ABCDEF"} gibt \texttt{"BCDEF"} zurück.151\end{itemize}152153\subsection{Let und where}\xindex{let (Haskell)@\texttt{let} (Haskell)}\xindex{where (Haskell)@\texttt{where} (Haskell)}%154\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/let-where-bindung.hs}155156\subsection{Funktionskomposition}\xindex{. (Haskell)}\xindex{Funktionskomposition}%157In Haskell funktioniert Funktionskomposition mit einem Punkt:158159\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/function-composition.hs}160161Dabei ergibt \texttt{h (-3)} in der mathematischen Notation162\[(g \circ f) (-3) = f(g(-3)) = f(-4) = 16\]163und \texttt{i (-3)} ergibt164\[(f \circ g) (-3) = g(f(-3)) = g(9) = 8\]165Es ist also anzumerken, dass die Reihenfolge der mathematischen Konvention entspricht.166167\subsection{\$ (Dollar-Zeichen) und ++}\xindex{\$ (Haskell)}\xindex{++ (Haskell)@\texttt{++} (Haskell)}%168Das Dollar-Zeichen \$ dient in Haskell dazu Klammern zu vermeiden. So sind die169folgenden Zeilen äquivalent:170171\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/dollar-example.hs}172173Das doppelte Plus (\texttt{++}) wird verwendet um Listen mit einander zu verbinden.174175\subsection{Logische Operatoren}\xindex{Haskell!Logische Operatoren}176177\begin{table}[H]178\centering179\begin{tabular}{CCCC}180UND & ODER & Wahr & Falsch \\ \hline\hline181\&\& & || & True & False \\[4ex]182GLEICH & UNGLEICH & NICHT & ~ \\ \hline\hline183== & /= & not & ~ \\184\end{tabular}185\caption{Logische Operatoren in Haskell}\xindex{Logische Operatoren!Haskell}186\end{table}187188\section{Typen}189\subsection{Standard-Typen}190Haskell kennt einige Basis-Typen:191\begin{itemize}192\item \textbf{Int}: Ganze Zahlen. Der Zahlenbereich kann je nach Implementierung variieren,193aber der Haskell-Standart garantiert, dass das Intervall194$[-2^{29}, 2^{29}-1]$ abgedeckt wird.195\item \textbf{Integer}: beliebig große ganze Zahlen196\item \textbf{Float}: Fließkommazahlen197\item \textbf{Double}: Fließkommazahlen mit doppelter Präzision198\item \textbf{Bool}: Wahrheitswerte199\item \textbf{Char}: Unicode-Zeichen200\end{itemize}201202Des weiteren gibt es einige strukturierte Typen:203\begin{itemize}204\item Listen: z.~B. \texttt{[1,2,3]}205\item Tupel: z.~B. \texttt{(1,'a',2)}206\item Brüche (Fractional, RealFrac)207\item Summen-Typen: Typen mit mehreren möglichen Repräsentationen208\end{itemize}209210\subsection{Typinferenz}211In Haskell werden Typen aus den Operationen geschlossfolgert. Dieses212Schlussfolgern der Typen, die nicht explizit angegeben werden müssen,213nennt man \textbf{Typinferent}\xindex{Typinferenz}.214215216Haskell kennt die Typen aus \cref{fig:haskell-type-hierarchy}.217218Ein paar Beispiele zur Typinferenz:219\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/typinferenz.sh}220221\begin{figure}[htp]222\centering223\resizebox{0.9\linewidth}{!}{\input{figures/haskell-type-classes.tex}}224\caption{Hierarchie der Haskell Standardklassen}225\label{fig:haskell-type-hierarchy}226\end{figure}227228\subsection{type}\xindex{type (Haskell)@\texttt{type} (Haskell)}%229Mit \texttt{type} können Typsynonyme erstellt werden:230231\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/alt-types.hs}232233\subsection{data}\xindex{data (Haskell)@\texttt{data} (Haskell)}%234Mit dem Schlüsselwort \texttt{data} können algebraische Datentypen\xindex{Datentyp!algebraischer}235erzeugt werden:236237\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/data-example.hs}238239\section{Lazy Evaluation}\xindex{Lazy Evaluation}%240Haskell wertet Ausdrücke nur aus, wenn es nötig ist.241242\begin{beispiel}[Lazy Evaluation]243Obwohl der folgende Ausdruck einen Teilausdruck hat, der einen Fehler zurückgeben244würde, kann er aufgrund der Lazy Evaluation zu 2 evaluiert werden:245\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=lazy-evaluation.hs]{haskell}{scripts/haskell/lazy-evaluation.hs}246\end{beispiel}247248\section{Beispiele}249250\subsection{Primzahlsieb}\xindex{Primzahlsieb}%251\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=primzahlsieb.hs]{haskell}{scripts/haskell/primzahlsieb.hs}252253\subsection{Quicksort}\xindex{filter (Haskell)@\texttt{filter} (Haskell)}%254\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=qsort.hs]{haskell}{scripts/haskell/qsort.hs}255256\begin{itemize}257\item Die leere Liste ergibt sortiert die leere Liste.258\item Wähle das erste Element \texttt{p} als Pivotelement und259teile die restliche Liste \texttt{ps} in kleinere und260gleiche sowie in größere Elemente mit \texttt{filter} auf.261Konkateniere diese beiden Listen mit \texttt{++}.262\end{itemize}263264Durch das Ausnutzen von Unterversorgung\xindex{Unterversorgung} lässt265sich das ganze sogar noch kürzer schreiben:266267\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=qsort.hs]{haskell}{scripts/haskell/qsort-unterversorg.hs}268269\subsection{Fibonacci}\xindex{Fibonacci}270\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=fibonacci.hs]{haskell}{scripts/haskell/fibonacci.hs}271\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=fibonacci-akk.hs]{haskell}{scripts/haskell/fibonacci-akk.hs}272\xindex{zipWith (Haskell)@\texttt{zipWith} (Haskell)}273\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=fibonacci-zip.hs]{haskell}{scripts/haskell/fibonacci-zip.hs}274\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=fibonacci-pattern-matching.hs]{haskell}{scripts/haskell/fibonacci-pattern-matching.hs}275276Die unendliche Liste alle Fibonacci-Zahlen, also der Fibonacci-\textit{Stream}\xindex{Stream}277wird wie folgt erzeugt:278279\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=fibonacci-stream.hs]{haskell}{scripts/haskell/fibonacci-stream.hs}280281\subsection{Polynome}\xindex{Polynome}\xindex{zipWith (Haskell)@\texttt{zipWith} (Haskell)}\xindex{Folds!foldr (Haskell)@\texttt{foldr} (Haskell)}\xindex{tail (Haskell)@\texttt{tail} (Haskell)}%282\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=polynome.hs]{haskell}{scripts/haskell/polynome.hs}283284\subsection{Hirsch-Index}\xindex{Hirsch-Index}\xindex{Ord}\xindex{Num}%285\textbf{Parameter}: Eine Liste $L$ von Zahlen aus $\mdn$\\286\textbf{Rückgabe}: $\max \Set{n \in \mdn | n \leq \|\Set{i \in L | i \geq n}\|}$287288\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=hirsch-index.hs]{haskell}{scripts/haskell/hirsch-index.hs}289290\subsection{Lauflängencodierung}\xindex{Lauflängencodierung}\xindex{splitWhen}\xindex{group}\xindex{concat}%291292\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=lauflaengencodierung.hs]{haskell}{scripts/haskell/lauflaengencodierung.hs}293294\subsection{Intersections}\xindex{Intersections}\xindex{List-Comprehension}%295296\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=intersect.hs]{haskell}{scripts/haskell/intersect.hs}297298\subsection{Funktionen höherer Ordnung}\xindex{Folds}\xindex{Folds!foldl (Haskell)@\texttt{foldl} (Haskell)}\xindex{Folds!foldr (Haskell)@\texttt{foldr} (Haskell)}\label{bsp:foldl-und-foldr}299\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=folds.hs]{haskell}{scripts/haskell/folds.hs}300301\subsection{Chruch-Zahlen}302\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=church.hs]{haskell}{scripts/haskell/church.hs}303304\subsection{Trees}\xindex{tree}\xindex{map!tree}%305Einen Binärbaum kann man in Haskell so definieren:306307\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/binary-tree.hs}308309Einen allgemeinen Baum so:310311\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/general-tree.hs}312313Hier ist \texttt{t} der polymorphe Typ des Baumes. \texttt{t} gibt also an welche314Elemente der Baum enthält.315316Man kann auf einem solchen Baum auch eine Variante von \texttt{map} und317\texttt{reduce} definieren,318also eine Funktion \texttt{mapT}, die eine weitere Funktion \texttt{f} auf jeden319Knoten anwendet:320321\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/mapt.hs}322323\subsection{Standard Prelude}324Hier sind die Definitionen eininger wichtiger Funktionen:325\xindex{zipWith (Haskell)@\texttt{zipWith} (Haskell)}\xindex{zip (Haskell)@\texttt{zip} (Haskell)}326\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/standard-definitions.hs}327328\section{Weitere Informationen}329\begin{itemize}330\item \href{http://hackage.haskell.org/package/base-4.6.0.1}{\path{hackage.haskell.org/package/base-4.6.0.1}}: Referenz331\item \href{http://www.haskell.org/hoogle/}{\path{haskell.org/hoogle}}: Suchmaschine für das Haskell-Manual332\item \href{http://wiki.ubuntuusers.de/Haskell}{\path{wiki.ubuntuusers.de/Haskell}}: Hinweise zur Installation von Haskell unter Ubuntu333\item \href{http://learnyouahaskell.com/chapters}{learnyouahaskell.com/chapters}334\end{itemize}335336\index{Haskell|)}337338339340