Haskell.tex 15 KB

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