Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download

📚 The CoCalc Library - books, templates and other resources

132930 views
License: OTHER
1
%!TEX root = Programmierparadigmen.tex
2
\chapter{Haskell}
3
\index{Haskell|(}
4
Haskell ist eine funktionale Programmiersprache, die 1990 in Version~1.0 veröffentlicht
5
wurde. Namensgeber ist Haskell Brooks Curry, der die mathematischen Grundlagen der funktionalen Programmierung entwickelte.
6
7
Wichtige Konzepte sind:
8
\begin{enumerate}
9
\item Funktionen höherer Ordnung
10
\item anonyme Funktionen (sog. Lambda-Funktionen)
11
\item Pattern Matching
12
\item Unterversorgung
13
\item Typinferenz
14
\end{enumerate}
15
16
Haskell kann mit \enquote{Glasgow Haskell Compiler} mittels
17
\texttt{ghci} interpretiert und mittels
18
19
\section{Erste Schritte}
20
Haskell kann unter \href{http://www.haskell.org/platform/}{\path{www.haskell.org/platform/}}
21
für alle Plattformen heruntergeladen werden. Unter Debian-Systemen
22
ist das Paket \texttt{ghc} bzw. \texttt{haskell-platform} relevant.
23
24
\subsection{Hello World}
25
Speichere folgenden Quelltext als \texttt{hello-world.hs}:
26
\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=hello-world.hs]{haskell}{scripts/haskell/hello-world.hs}
27
28
Kompiliere ihn mit \texttt{ghc -o hello hello-world.hs}. Es wird eine
29
ausführbare Datei erzeugt.
30
31
Alternativ kann es direkt mit \texttt{runghc hello-world.hs} ausgeführt werden.
32
33
\section{Syntax}
34
\subsection{Kommentare}\xindex{Kommentare!Haskell}
35
In Haskell werden kommentare durch \verb+--+ begonnen.
36
37
\subsection{Klammern und Funktionsdeklaration}
38
Haskell verzichtet an vielen Stellen auf Klammern. So werden im
39
Folgenden die Funktionen $f(x) := \frac{\sin x}{x}$ und $g(x) := x \cdot f(x^2)$
40
definiert:
41
42
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/einfaches-beispiel-klammern.hs}
43
44
Die Funktionsdeklarationen mit den Typen sind nicht notwendig, da
45
die Typen aus den benutzten Funktionen abgeleitet werden.
46
47
Zu lesen ist die Deklaration wie folgt:
48
49
\begin{center}
50
\texttt{[Funktionsname] :: \texttt{[Typendefinitionen]} => \texttt{Signatur}}
51
\end{center}
52
53
\begin{itemize}
54
\item[T. Def.] Die Funktion \texttt{f} benutzt als Parameter bzw. Rückgabewert
55
einen Typen. Diesen Typen nennen wir \texttt{a} und er ist
56
vom Typ \texttt{Floating}. Auch \texttt{b}, \texttt{wasweisich}
57
oder etwas ähnliches wäre ok.
58
\item[Signatur] Die Signatur liest man am einfachsten von hinten:
59
\begin{itemize}
60
\item \texttt{f} bildet auf einen Wert vom Typ \texttt{a} ab und
61
\item \texttt{f} hat genau einen Parameter \texttt{a}
62
\end{itemize}
63
\end{itemize}
64
65
\todo[inline]{Gibt es Funktionsdeklarationen, die bis auf Wechsel des Namens und der Reihenfolge äquivalent sind?}
66
67
\subsection{if / else}\xindex{Haskell!if@\texttt{if}}%
68
Das folgende Beispiel definiert den Binomialkoeffizienten (vgl. \cref{bsp:binomialkoeffizient}):
69
70
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/binomialkoeffizient.hs}
71
72
Das könnte man auch mit sog. Guards machen:\xindex{Guard}
73
74
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/binomialkoeffizient-guard.hs}
75
76
\subsection{Rekursion}
77
Die Fakultätsfunktion wurde wie folgt implementiert:
78
\[fak(n) := \begin{cases}
79
1 &\text{falls } n=0\\
80
n \cdot fak(n) &\text{sonst}
81
\end{cases}\]
82
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/fakultaet.hs}
83
84
Diese Implementierung benötigt $\mathcal{O}(n)$ rekursive Aufrufe und
85
hat einen Speicherverbrauch von $\mathcal{O}(n)$. Durch einen
86
\textbf{Akkumulator}\xindex{Akkumulator} kann dies verhindert werden:
87
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/fakultaet-akkumulator.hs}
88
89
\subsection{Listen}
90
Listen sind in Haskell 0-indiziert, d.~h. das erste Element jeder Liste hat
91
den Index 0.
92
93
\begin{itemize}
94
\item \texttt{[]} erzeugt die leere Liste,
95
\item \texttt{[1,2,3]} erzeugt eine Liste mit den Elementen $1, 2, 3$
96
\item \texttt{:}\xindex{: (Haskell)} wird \textbf{cons}\xindex{cons} genannt und ist
97
der Listenkonstruktor.
98
\item \texttt{list !! i}\xindex{"!"! (Haskell)} gibt das $i$-te Element von \texttt{list} zurück.
99
\item \texttt{head list} gibt den Kopf von \texttt{list} zurück,
100
\texttt{tail list} den Rest:
101
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/list-basic.sh}
102
\item \texttt{last [1,9,1,3]} gibt 3 zurück.
103
\item \texttt{length list} gibt die Anzahl der Elemente in \texttt{list} zurück.
104
\item \texttt{maximum [1,9,1,3]} gibt 9 zurück (analog: \texttt{minimum}).
105
\item \texttt{null list} prüft, ob \texttt{list} leer ist.
106
\item \texttt{take 3 [1,2,3,4,5]} gibt \texttt{[1,2,3]} zurück.
107
\item \texttt{drop 3 [1,2,3,4,5]} gibt \texttt{[4,5]} zurück.
108
\item \texttt{reverse [1,9,1,3]} gibt \texttt{[3,1,9,1]} zurück.
109
\item \texttt{elem item list} gibt zurück, ob sich \texttt{item} in \texttt{list} befindet.
110
\end{itemize}
111
112
\subsubsection{Beispiel in der interaktiven Konsole}
113
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/listenoperationen.sh}
114
115
\subsubsection{List-Comprehensions}\xindex{List-Comprehension}%
116
List-Comprehensions sind kurzschreibweisen für Listen, die sich an
117
der Mengenschreibweise in der Mathematik orientieren. So entspricht
118
die Menge
119
\begin{align*}
120
myList &= \Set{1,2,3,4,5,6}\\
121
test &= \Set{x \in myList | x > 2}
122
\end{align*}
123
in etwa folgendem Haskell-Code:
124
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/list-comprehensions.sh}
125
126
\begin{beispiel}[List-Comprehension]
127
Das folgende Beispiel zeigt, wie man mit List-Comprehensions die unendliche
128
Liste aller pythagoreischen Tripels erstellen kann:
129
130
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/pythagorean-triples.hs}
131
\end{beispiel}
132
133
\begin{beispiel}[List-Comprehension]
134
Das folgende Beispiel zeigt, wie man die unendlich große Menge
135
136
\[\Set{p^i | p \text{ Primzahl}, 1 \leq i \leq n}\]
137
138
erstellt:
139
140
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/primepowers.hs}
141
142
Dabei ist die Reihenfolge wichtig. Würde man zuerst die \verb+i+ und dann
143
die Primzahlen notieren, würde Haskell versuchen erst alle Primzahlen durch
144
zu gehen. Da dies eine unendliche Liste ist, würde also niemals die zweite
145
Potenz einer Primzahl auftauchen.
146
\end{beispiel}
147
148
\subsection{Strings}
149
\begin{itemize}
150
\item Strings sind Listen von Zeichen:\\
151
\texttt{tail "ABCDEF"} gibt \texttt{"BCDEF"} zurück.
152
\end{itemize}
153
154
\subsection{Let und where}\xindex{let (Haskell)@\texttt{let} (Haskell)}\xindex{where (Haskell)@\texttt{where} (Haskell)}%
155
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/let-where-bindung.hs}
156
157
\subsection{Funktionskomposition}\xindex{. (Haskell)}\xindex{Funktionskomposition}%
158
In Haskell funktioniert Funktionskomposition mit einem Punkt:
159
160
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/function-composition.hs}
161
162
Dabei ergibt \texttt{h (-3)} in der mathematischen Notation
163
\[(g \circ f) (-3) = f(g(-3)) = f(-4) = 16\]
164
und \texttt{i (-3)} ergibt
165
\[(f \circ g) (-3) = g(f(-3)) = g(9) = 8\]
166
Es ist also anzumerken, dass die Reihenfolge der mathematischen Konvention entspricht.
167
168
\subsection{\$ (Dollar-Zeichen) und ++}\xindex{\$ (Haskell)}\xindex{++ (Haskell)@\texttt{++} (Haskell)}%
169
Das Dollar-Zeichen \$ dient in Haskell dazu Klammern zu vermeiden. So sind die
170
folgenden Zeilen äquivalent:
171
172
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/dollar-example.hs}
173
174
Das doppelte Plus (\texttt{++}) wird verwendet um Listen mit einander zu verbinden.
175
176
\subsection{Logische Operatoren}\xindex{Haskell!Logische Operatoren}
177
178
\begin{table}[H]
179
\centering
180
\begin{tabular}{CCCC}
181
UND & ODER & Wahr & Falsch \\ \hline\hline
182
\&\& & || & True & False \\[4ex]
183
GLEICH & UNGLEICH & NICHT & ~ \\ \hline\hline
184
== & /= & not & ~ \\
185
\end{tabular}
186
\caption{Logische Operatoren in Haskell}\xindex{Logische Operatoren!Haskell}
187
\end{table}
188
189
\section{Typen}
190
\subsection{Standard-Typen}
191
Haskell kennt einige Basis-Typen:
192
\begin{itemize}
193
\item \textbf{Int}: Ganze Zahlen. Der Zahlenbereich kann je nach Implementierung variieren,
194
aber der Haskell-Standart garantiert, dass das Intervall
195
$[-2^{29}, 2^{29}-1]$ abgedeckt wird.
196
\item \textbf{Integer}: beliebig große ganze Zahlen
197
\item \textbf{Float}: Fließkommazahlen
198
\item \textbf{Double}: Fließkommazahlen mit doppelter Präzision
199
\item \textbf{Bool}: Wahrheitswerte
200
\item \textbf{Char}: Unicode-Zeichen
201
\end{itemize}
202
203
Des weiteren gibt es einige strukturierte Typen:
204
\begin{itemize}
205
\item Listen: z.~B. \texttt{[1,2,3]}
206
\item Tupel: z.~B. \texttt{(1,'a',2)}
207
\item Brüche (Fractional, RealFrac)
208
\item Summen-Typen: Typen mit mehreren möglichen Repräsentationen
209
\end{itemize}
210
211
\subsection{Typinferenz}
212
In Haskell werden Typen aus den Operationen geschlossfolgert. Dieses
213
Schlussfolgern der Typen, die nicht explizit angegeben werden müssen,
214
nennt man \textbf{Typinferent}\xindex{Typinferenz}.
215
216
217
Haskell kennt die Typen aus \cref{fig:haskell-type-hierarchy}.
218
219
Ein paar Beispiele zur Typinferenz:
220
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/typinferenz.sh}
221
222
\begin{figure}[htp]
223
\centering
224
\resizebox{0.9\linewidth}{!}{\input{figures/haskell-type-classes.tex}}
225
\caption{Hierarchie der Haskell Standardklassen}
226
\label{fig:haskell-type-hierarchy}
227
\end{figure}
228
229
\subsection{type}\xindex{type (Haskell)@\texttt{type} (Haskell)}%
230
Mit \texttt{type} können Typsynonyme erstellt werden:
231
232
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/alt-types.hs}
233
234
\subsection{data}\xindex{data (Haskell)@\texttt{data} (Haskell)}%
235
Mit dem Schlüsselwort \texttt{data} können algebraische Datentypen\xindex{Datentyp!algebraischer}
236
erzeugt werden:
237
238
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/data-example.hs}
239
240
\section{Lazy Evaluation}\xindex{Lazy Evaluation}%
241
Haskell wertet Ausdrücke nur aus, wenn es nötig ist.
242
243
\begin{beispiel}[Lazy Evaluation]
244
Obwohl der folgende Ausdruck einen Teilausdruck hat, der einen Fehler zurückgeben
245
würde, kann er aufgrund der Lazy Evaluation zu 2 evaluiert werden:
246
\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=lazy-evaluation.hs]{haskell}{scripts/haskell/lazy-evaluation.hs}
247
\end{beispiel}
248
249
\section{Beispiele}
250
251
\subsection{Primzahlsieb}\xindex{Primzahlsieb}%
252
\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=primzahlsieb.hs]{haskell}{scripts/haskell/primzahlsieb.hs}
253
254
\subsection{Quicksort}\xindex{filter (Haskell)@\texttt{filter} (Haskell)}%
255
\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=qsort.hs]{haskell}{scripts/haskell/qsort.hs}
256
257
\begin{itemize}
258
\item Die leere Liste ergibt sortiert die leere Liste.
259
\item Wähle das erste Element \texttt{p} als Pivotelement und
260
teile die restliche Liste \texttt{ps} in kleinere und
261
gleiche sowie in größere Elemente mit \texttt{filter} auf.
262
Konkateniere diese beiden Listen mit \texttt{++}.
263
\end{itemize}
264
265
Durch das Ausnutzen von Unterversorgung\xindex{Unterversorgung} lässt
266
sich das ganze sogar noch kürzer schreiben:
267
268
\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=qsort.hs]{haskell}{scripts/haskell/qsort-unterversorg.hs}
269
270
\subsection{Fibonacci}\xindex{Fibonacci}
271
\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=fibonacci.hs]{haskell}{scripts/haskell/fibonacci.hs}
272
\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=fibonacci-akk.hs]{haskell}{scripts/haskell/fibonacci-akk.hs}
273
\xindex{zipWith (Haskell)@\texttt{zipWith} (Haskell)}
274
\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=fibonacci-zip.hs]{haskell}{scripts/haskell/fibonacci-zip.hs}
275
\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=fibonacci-pattern-matching.hs]{haskell}{scripts/haskell/fibonacci-pattern-matching.hs}
276
277
Die unendliche Liste alle Fibonacci-Zahlen, also der Fibonacci-\textit{Stream}\xindex{Stream}
278
wird wie folgt erzeugt:
279
280
\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=fibonacci-stream.hs]{haskell}{scripts/haskell/fibonacci-stream.hs}
281
282
\subsection{Polynome}\xindex{Polynome}\xindex{zipWith (Haskell)@\texttt{zipWith} (Haskell)}\xindex{Folds!foldr (Haskell)@\texttt{foldr} (Haskell)}\xindex{tail (Haskell)@\texttt{tail} (Haskell)}%
283
\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=polynome.hs]{haskell}{scripts/haskell/polynome.hs}
284
285
\subsection{Hirsch-Index}\xindex{Hirsch-Index}\xindex{Ord}\xindex{Num}%
286
\textbf{Parameter}: Eine Liste $L$ von Zahlen aus $\mdn$\\
287
\textbf{Rückgabe}: $\max \Set{n \in \mdn | n \leq \|\Set{i \in L | i \geq n}\|}$
288
289
\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=hirsch-index.hs]{haskell}{scripts/haskell/hirsch-index.hs}
290
291
\subsection{Lauflängencodierung}\xindex{Lauflängencodierung}\xindex{splitWhen}\xindex{group}\xindex{concat}%
292
293
\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=lauflaengencodierung.hs]{haskell}{scripts/haskell/lauflaengencodierung.hs}
294
295
\subsection{Intersections}\xindex{Intersections}\xindex{List-Comprehension}%
296
297
\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=intersect.hs]{haskell}{scripts/haskell/intersect.hs}
298
299
\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}
300
\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=folds.hs]{haskell}{scripts/haskell/folds.hs}
301
302
\subsection{Chruch-Zahlen}
303
\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=church.hs]{haskell}{scripts/haskell/church.hs}
304
305
\subsection{Trees}\xindex{tree}\xindex{map!tree}%
306
Einen Binärbaum kann man in Haskell so definieren:
307
308
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/binary-tree.hs}
309
310
Einen allgemeinen Baum so:
311
312
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/general-tree.hs}
313
314
Hier ist \texttt{t} der polymorphe Typ des Baumes. \texttt{t} gibt also an welche
315
Elemente der Baum enthält.
316
317
Man kann auf einem solchen Baum auch eine Variante von \texttt{map} und
318
\texttt{reduce} definieren,
319
also eine Funktion \texttt{mapT}, die eine weitere Funktion \texttt{f} auf jeden
320
Knoten anwendet:
321
322
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/mapt.hs}
323
324
\subsection{Standard Prelude}
325
Hier sind die Definitionen eininger wichtiger Funktionen:
326
\xindex{zipWith (Haskell)@\texttt{zipWith} (Haskell)}\xindex{zip (Haskell)@\texttt{zip} (Haskell)}
327
\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/standard-definitions.hs}
328
329
\section{Weitere Informationen}
330
\begin{itemize}
331
\item \href{http://hackage.haskell.org/package/base-4.6.0.1}{\path{hackage.haskell.org/package/base-4.6.0.1}}: Referenz
332
\item \href{http://www.haskell.org/hoogle/}{\path{haskell.org/hoogle}}: Suchmaschine für das Haskell-Manual
333
\item \href{http://wiki.ubuntuusers.de/Haskell}{\path{wiki.ubuntuusers.de/Haskell}}: Hinweise zur Installation von Haskell unter Ubuntu
334
\item \href{http://learnyouahaskell.com/chapters}{learnyouahaskell.com/chapters}
335
\end{itemize}
336
337
\index{Haskell|)}
338
339
340