Haskell.tex 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  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. \begin{itemize}
  70. \item \texttt{[]} erzeugt die leere Liste,
  71. \item \texttt{[1,2,3]} erzeugt eine Liste mit den Elementen $1, 2, 3$
  72. \item \texttt{:}\xindex{: (Haskell)} wird \textbf{cons}\xindex{cons} genannt und ist
  73. der Listenkonstruktor.
  74. \item \texttt{list !! i}\xindex{"!"! (Haskell)} gibt das $i$-te Element von \texttt{list} zurück.
  75. \item \texttt{head list} gibt den Kopf von \texttt{list} zurück,
  76. \texttt{tail list} den Rest:
  77. \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/list-basic.sh}
  78. \item \texttt{last [1,9,1,3]} gibt 3 zurück.
  79. \item \texttt{length list} gibt die Anzahl der Elemente in \texttt{list} zurück.
  80. \item \texttt{maximum [1,9,1,3]} gibt 9 zurück (analog: \texttt{minimum}).
  81. \item \texttt{null list} prüft, ob \texttt{list} leer ist.
  82. \item \texttt{take 3 [1,2,3,4,5]} gibt \texttt{[1,2,3]} zurück.
  83. \item \texttt{drop 3 [1,2,3,4,5]} gibt \texttt{[4,5]} zurück.
  84. \item \texttt{reverse [1,9,1,3]} gibt \texttt{[3,1,9,1]} zurück.
  85. \item \texttt{elem item list} gibt zurück, ob sich \texttt{item} in \texttt{list} befindet.
  86. \end{itemize}
  87. \subsubsection{Beispiel in der interaktiven Konsole}
  88. \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/listenoperationen.sh}
  89. \subsubsection{List-Comprehensions}\xindex{List-Comprehension}%
  90. List-Comprehensions sind kurzschreibweisen für Listen, die sich an
  91. der Mengenschreibweise in der Mathematik orientieren. So entspricht
  92. die Menge
  93. \begin{align*}
  94. myList &= \Set{1,2,3,4,5,6}\\
  95. test &= \Set{x \in myList | x > 2}
  96. \end{align*}
  97. in etwa folgendem Haskell-Code:
  98. \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/list-comprehensions.sh}
  99. \begin{beispiel}[List-Comprehension]
  100. Das folgende Beispiel zeigt, wie man mit List-Comprehensions die unendliche
  101. Liste aller pythagoreischen Tripels erstellen kann:
  102. \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/pythagorean-triples.hs}
  103. \end{beispiel}
  104. \begin{beispiel}[List-Comprehension]
  105. Das folgende Beispiel zeigt, wie man die unendlich große Menge
  106. \[\Set{p^i | p \text{ Primzahl}, 1 \leq i \leq n}\]
  107. erstellt:
  108. \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/primepowers.hs}
  109. Dabei ist die Reihenfolge wichtig. Würde man zuerst die \verb+i+ und dann
  110. die Primzahlen notieren, würde Haskell versuchen erst alle Primzahlen durch
  111. zu gehen. Da dies eine unendliche Liste ist, würde also niemals die zweite
  112. Potenz einer Primzahl auftauchen.
  113. \end{beispiel}
  114. \subsection{Strings}
  115. \begin{itemize}
  116. \item Strings sind Listen von Zeichen:\\
  117. \texttt{tail "ABCDEF"} gibt \texttt{"BCDEF"} zurück.
  118. \end{itemize}
  119. \subsection{Let und where}\xindex{let}\xindex{where}%
  120. \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/let-where-bindung.hs}
  121. \subsection{Funktionskomposition}\xindex{. (Haskell)}\xindex{Funktionskomposition}%
  122. In Haskell funktioniert Funktionskomposition mit einem Punkt:
  123. \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/function-composition.hs}
  124. Dabei ergibt \texttt{h (-3)} in der mathematischen Notation
  125. \[(g \circ f) (-3) = f(g(-3)) = f(-4) = 16\]
  126. und \texttt{i (-3)} ergibt
  127. \[(f \circ g) (-3) = g(f(-3)) = g(9) = 8\]
  128. Es ist also anzumerken, dass die Reihenfolge der mathematischen Konvention entspricht.
  129. \subsection{\$ (Dollar-Zeichen) und ++}\xindex{\$ (Haskell)}\xindex{++ (Haskell)@\texttt{++} (Haskell)}%
  130. Das Dollar-Zeichen \$ dient in Haskell dazu Klammern zu vermeiden. So sind die
  131. folgenden Zeilen äquivalent:
  132. \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/dollar-example.hs}
  133. Das doppelte Plus (\texttt{++}) wird verwendet um Listen mit einander zu verbinden.
  134. \subsection{Logische Operatoren}\xindex{Haskell!Logische Operatoren}
  135. \begin{table}[H]
  136. \centering
  137. \begin{tabular}{CCCC}
  138. UND & ODER & Wahr & Falsch \\ \hline\hline
  139. \&\& & || & True & False \\[4ex]
  140. GLEICH & UNGLEICH & NICHT & ~ \\ \hline\hline
  141. == & /= & not & ~ \\
  142. \end{tabular}
  143. \caption{Logische Operatoren in Haskell}\xindex{Logische Operatoren!Haskell}
  144. \end{table}
  145. \section{Typen}
  146. \subsection{Standard-Typen}
  147. Haskell kennt einige Basis-Typen:
  148. \begin{itemize}
  149. \item \textbf{Int}: Ganze Zahlen. Der Zahlenbereich kann je nach Implementierung variieren,
  150. aber der Haskell-Standart garantiert, dass das Intervall
  151. $[-2^{29}, 2^{29}-1]$ abgedeckt wird.
  152. \item \textbf{Integer}: beliebig große ganze Zahlen
  153. \item \textbf{Float}: Fließkommazahlen
  154. \item \textbf{Double}: Fließkommazahlen mit doppelter Präzision
  155. \item \textbf{Bool}: Wahrheitswerte
  156. \item \textbf{Char}: Unicode-Zeichen
  157. \end{itemize}
  158. Des weiteren gibt es einige strukturierte Typen:
  159. \begin{itemize}
  160. \item Listen: z.~B. \texttt{[1,2,3]}
  161. \item Tupel: z.~B. \texttt{(1,'a',2)}
  162. \item Brüche (Fractional, RealFrac)
  163. \item Summen-Typen: Typen mit mehreren möglichen Repräsentationen
  164. \end{itemize}
  165. \subsection{Typinferenz}
  166. In Haskell werden Typen aus den Operationen geschlossfolgert. Dieses
  167. Schlussfolgern der Typen, die nicht explizit angegeben werden müssen,
  168. nennt man \textbf{Typinferent}\xindex{Typinferenz}.
  169. Haskell kennt die Typen aus \cref{fig:haskell-type-hierarchy}.
  170. Ein paar Beispiele zur Typinferenz:
  171. \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/typinferenz.sh}
  172. \begin{figure}[htp]
  173. \centering
  174. \resizebox{0.9\linewidth}{!}{\input{figures/haskell-type-classes.tex}}
  175. \caption{Hierarchie der Haskell Standardklassen}
  176. \label{fig:haskell-type-hierarchy}
  177. \end{figure}
  178. \subsection{type}\xindex{type}%
  179. Mit \texttt{type} können Typsynonyme erstellt werden:
  180. \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/alt-types.hs}
  181. \subsection{data}\xindex{data}%
  182. Mit dem Schlüsselwort \texttt{data} können algebraische Datentypen\xindex{Datentyp!algebraischer}
  183. erzeugt werden:
  184. \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/data-example.hs}
  185. \section{Lazy Evaluation}\xindex{Lazy Evaluation}%
  186. Haskell wertet Ausdrücke nur aus, wenn es nötig ist.
  187. \begin{beispiel}[Lazy Evaluation]
  188. Obwohl der folgende Ausdruck einen Teilausdruck hat, der einen Fehler zurückgeben
  189. würde, kann er aufgrund der Lazy Evaluation zu 2 evaluiert werden:
  190. \inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=lazy-evaluation.hs]{haskell}{scripts/haskell/lazy-evaluation.hs}
  191. \end{beispiel}
  192. \section{Beispiele}
  193. \subsection{Primzahlsieb}\xindex{Primzahlsieb}%
  194. \inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=primzahlsieb.hs]{haskell}{scripts/haskell/primzahlsieb.hs}
  195. \subsection{Quicksort}\xindex{filter (Haskell)@\texttt{filter} (Haskell)}%
  196. \inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=qsort.hs]{haskell}{scripts/haskell/qsort.hs}
  197. \begin{itemize}
  198. \item Die leere Liste ergibt sortiert die leere Liste.
  199. \item Wähle das erste Element \texttt{p} als Pivotelement und
  200. teile die restliche Liste \texttt{ps} in kleinere und
  201. gleiche sowie in größere Elemente mit \texttt{filter} auf.
  202. Konkateniere diese beiden Listen mit \texttt{++}.
  203. \end{itemize}
  204. Durch das Ausnutzen von Unterversorgung\xindex{Unterversorgung} lässt
  205. sich das ganze sogar noch kürzer schreiben:
  206. \inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=qsort.hs]{haskell}{scripts/haskell/qsort-unterversorg.hs}
  207. \subsection{Fibonacci}\xindex{Fibonacci}
  208. \inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=fibonacci.hs]{haskell}{scripts/haskell/fibonacci.hs}
  209. \inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=fibonacci-akk.hs]{haskell}{scripts/haskell/fibonacci-akk.hs}
  210. \xindex{zipWith}
  211. \inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=fibonacci-zip.hs]{haskell}{scripts/haskell/fibonacci-zip.hs}
  212. \inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=fibonacci-pattern-matching.hs]{haskell}{scripts/haskell/fibonacci-pattern-matching.hs}
  213. Die unendliche Liste alle Fibonacci-Zahlen, also der Fibonacci-\textit{Stream}\xindex{Stream}
  214. wird wie folgt erzeugt:
  215. \inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=fibonacci-stream.hs]{haskell}{scripts/haskell/fibonacci-stream.hs}
  216. \subsection{Polynome}\xindex{Polynome}\xindex{zipWith}\xindex{foldr}\xindex{tail}%
  217. \inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=polynome.hs]{haskell}{scripts/haskell/polynome.hs}
  218. \subsection{Hirsch-Index}\xindex{Hirsch-Index}\xindex{Ord}\xindex{Num}%
  219. \textbf{Parameter}: Eine Liste $L$ von Zahlen aus $\mdn$\\
  220. \textbf{Rückgabe}: $\max \Set{n \in \mdn | n \leq \|\Set{i \in L | i \geq n}\|}$
  221. \inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=hirsch-index.hs]{haskell}{scripts/haskell/hirsch-index.hs}
  222. \subsection{Lauflängencodierung}\xindex{Lauflängencodierung}\xindex{splitWhen}\xindex{group}\xindex{concat}%
  223. \inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=lauflaengencodierung.hs]{haskell}{scripts/haskell/lauflaengencodierung.hs}
  224. \subsection{Intersections}\xindex{Intersections}\xindex{List-Comprehension}%
  225. \inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=intersect.hs]{haskell}{scripts/haskell/intersect.hs}
  226. \subsection{Funktionen höherer Ordnung}\xindex{Folds}\xindex{foldl}\xindex{foldr}\label{bsp:foldl-und-foldr}
  227. \inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=folds.hs]{haskell}{scripts/haskell/folds.hs}
  228. \subsection{Chruch-Zahlen}
  229. \inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=church.hs]{haskell}{scripts/haskell/church.hs}
  230. \subsection{Trees}\xindex{tree}\xindex{map!tree}%
  231. Einen Binärbaum kann man in Haskell so definieren:
  232. \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/binary-tree.hs}
  233. Einen allgemeinen Baum so:
  234. \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/general-tree.hs}
  235. Hier ist \texttt{t} der polymorphe Typ des Baumes. \texttt{t} gibt also an welche
  236. Elemente der Baum enthält.
  237. Man kann auf einem solchen Baum auch eine Variante von \texttt{map} und
  238. \texttt{reduce} definieren,
  239. also eine Funktion \texttt{mapT}, die eine weitere Funktion \texttt{f} auf jeden
  240. Knoten anwendet:
  241. \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/mapt.hs}
  242. \subsection{Standard Prelude}
  243. Hier sind die Definitionen eininger wichtiger Funktionen:
  244. \xindex{zipWith}\xindex{zip}
  245. \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/standard-definitions.hs}
  246. \section{Weitere Informationen}
  247. \begin{itemize}
  248. \item \href{http://hackage.haskell.org/package/base-4.6.0.1}{\path{hackage.haskell.org/package/base-4.6.0.1}}: Referenz
  249. \item \href{http://www.haskell.org/hoogle/}{\path{haskell.org/hoogle}}: Suchmaschine für das Haskell-Manual
  250. \item \href{http://wiki.ubuntuusers.de/Haskell}{\path{wiki.ubuntuusers.de/Haskell}}: Hinweise zur Installation von Haskell unter Ubuntu
  251. \end{itemize}
  252. \index{Haskell|)}