diff --git a/documents/Programmierparadigmen/Arbeitszeit.md b/documents/Programmierparadigmen/Arbeitszeit.md index 013a4d5..474fcee 100644 --- a/documents/Programmierparadigmen/Arbeitszeit.md +++ b/documents/Programmierparadigmen/Arbeitszeit.md @@ -7,3 +7,4 @@ in dem Erstellen dieses Skripts steckt: |01.02.2014 | 14:00 - 14:45 | Thoma | ASCII-Tabelle in C angefangen; Kapitel "Programmiersprachen" hinzugefügt; erste Definitionen |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 diff --git a/documents/Programmierparadigmen/Haskell.tex b/documents/Programmierparadigmen/Haskell.tex index e24de2f..5c0e7d5 100644 --- a/documents/Programmierparadigmen/Haskell.tex +++ b/documents/Programmierparadigmen/Haskell.tex @@ -40,12 +40,7 @@ definiert: \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/einfaches-beispiel-klammern.hs} \subsection{if / else} -Das folgende Beispiel definiert den Binomialkoeffizienten -\[\binom{n}{k} := \begin{cases} - 1 &\text{falls } k=0 \lor k = n\\ - \binom{n-1}{k-1}+\binom{n-1}{k} &\text{sonst} - \end{cases}\] -für $n,k \geq 0$: +Das folgende Beispiel definiert den Binomialkoeffizienten (vgl. \cref{bsp:binomialkoeffizient}) \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/binomialkoeffizient.hs} \inputminted[numbersep=5pt, tabsize=4]{bash}{scripts/haskell/compile-binom.sh} @@ -65,6 +60,9 @@ hat einen Speicherverbrauch von $\mathcal{O}(n)$. Durch einen \textbf{Akkumulator}\xindex{Akkumulator} kann dies verhindert werden: \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/fakultaet-akkumulator.hs} +\subsection{Listen} +\todo[inline]{Cons-Operator, Unendliche Listen} + \section{Beispiele} \subsection{Hello World} Speichere folgenden Quelltext als \texttt{hello-world.hs}: diff --git a/documents/Programmierparadigmen/Programmierparadigmen.pdf b/documents/Programmierparadigmen/Programmierparadigmen.pdf index 77078c7..b16c817 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 9c00181..f939ec9 100644 --- a/documents/Programmierparadigmen/Programmiertechniken.tex +++ b/documents/Programmierparadigmen/Programmiertechniken.tex @@ -1,5 +1,98 @@ \chapter{Programmiertechniken} \section{Rekursion} -\todo[inline]{Tail-Recursion} +\index{Rekursion|(} + +\begin{definition}[rekursive Funktion]\xindex{Funktion!rekursive} + Eine Funktion $f: X \rightarrow X$ heißt rekursiv definiert, + wenn in der Definition der Funktion die Funktion selbst wieder + steht. +\end{definition} + +\begin{beispiel}[rekursive Funktionen] + \begin{bspenum} + \item Fibonacci-Funktion:\xindex{Fibonacci-Funktion} + \begin{align*} + fib: \mdn_0 &\rightarrow \mdn_0\\ + fib(n) &= \begin{cases} + n &\text{falls } n \leq 1\\ + fib(n-1) + fib(n-2) &\text{sonst} + \end{cases} + \end{align*} + \item Fakultät:\xindex{Fakultät} + \begin{align*} + !: \mdn_0 &\rightarrow \mdn_0\\ + n! &= \begin{cases} + 1 &\text{falls } n \leq 1\\ + n\cdot (n-1)! &\text{sonst} + \end{cases} + \end{align*} + \item \label{bsp:binomialkoeffizient} Binomialkoeffizient:\xindex{Binomialkoeffizient} + \begin{align*} + \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} + \end{cases} + \end{align*} + \end{bspenum} +\end{beispiel} + +Ein Problem von rekursiven Funktionen in Computerprogrammen ist der +Speicherbedarf. Für jeden rekursiven Aufruf müssen alle Umgebungsvariablen +der aufrufenden Funktion (\enquote{stack frame}) gespeichert bleiben, +bis der rekursive Aufruf beendet ist. Im Fall der Fibonacci-Funktion +sieht ist der Call-Stack in \cref{fig:fib-callstack} abgebildet. + +\begin{figure}[htp] + \centering + \includegraphics*[width=0.5\linewidth, keepaspectratio]{figures/fib-callstack.png} + \caption{Call-Stack der Fibonacci-Funktion} + \label{fig:fib-callstack} +\end{figure} + +\begin{bemerkung} + Die Anzahl der rekursiven Aufrufe der Fibonacci-Funktion $f_C$ ist: + \[f_C(n) = \begin{cases} + 1 &\text{falls } n=0\\ + 2 \cdot fib(n) - 1 &\text{falls } n \geq 1 + \end{cases}\] +\end{bemerkung} +\begin{beweis}\leavevmode + \begin{itemize} + \item Offensichtlich gilt $f_C(0) = 1$ + \item Offensichtlich gilt $f_C(1) = 1 = 2 \cdot fib(1) - 1$ + \item Offensichtlich gilt $f_C(2) = 3 = 2 \cdot fib(2) - 1$ + \item Für $n \geq 3$: + \begin{align*} + f_C(n) &= 1 + f_C(n-1) + f_C(n-2)\\ + &= 1 + (2\cdot fib(n-1)-1) + (2 \cdot fib(n-2)-1)\\ + &=2\cdot (fib(n-1) + fib(n-2)) - 1\\ + &=2 \cdot fib(n) - 1 + \end{align*} + \end{itemize} +\end{beweis} + +Mit Hilfe der Formel von Moivre-Binet folgt: + +\[f_C \in \mathcal{O} \left (\frac{\varphi^n- \psi^n}{\varphi - \psi} \right) \text{ mit } \varphi := \frac{1+ \sqrt{5}}{2} \text{ und }\psi := 1 - \varphi\] + +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} + 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} + 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} + +\index{Rekursion|(} \section{Backtracking} diff --git a/documents/Programmierparadigmen/Vorwort.tex b/documents/Programmierparadigmen/Vorwort.tex index 79ac050..b6f639f 100644 --- a/documents/Programmierparadigmen/Vorwort.tex +++ b/documents/Programmierparadigmen/Vorwort.tex @@ -24,7 +24,8 @@ TODO \section*{Erforderliche Vorkenntnisse} Grundlegende Kenntnisse vom Programmieren, insbesondere mit Java, wie sie am KIT in \enquote{Programmieren} vermittelt werden, werden -vorausgesetzt. +vorausgesetzt. Außerdem könnte ein grundlegendes Verständnis für +das O-Kalkül aus \enquote{Grundbegriffe der Informatik} hilfreich sein. Die Unifikation wird wohl auch in \enquote{Formale Systeme} erklärt; das könnte also hier von Vorteil sein. diff --git a/documents/Programmierparadigmen/figures/fib-callstack.png b/documents/Programmierparadigmen/figures/fib-callstack.png new file mode 100644 index 0000000..5cc538a Binary files /dev/null and b/documents/Programmierparadigmen/figures/fib-callstack.png differ diff --git a/documents/Programmierparadigmen/scripts/programmiertechniken/fib-callstack b/documents/Programmierparadigmen/scripts/programmiertechniken/fib-callstack new file mode 100644 index 0000000..77da96e --- /dev/null +++ b/documents/Programmierparadigmen/scripts/programmiertechniken/fib-callstack @@ -0,0 +1,14 @@ +fib(3) +├ call(fib(2)) +│ ├─ call(fib(1)) +│ │ └─ return 1 +│ ├─ call(fib(0)) +│ │ └─ return 1 +│ └ return fib(1)+fib(0)=1+1 +├ call(fib(1)) +│ ├─ call(fib(1)) +│ │ └─ return 1 +│ ├─ call(fib(0)) +│ │ └─ return 1 +│ └ return fib(1)+fib(0)=1+1 +└ return fib(2)+fib(1)=2+2