Skip to content

Commit

Permalink
Noch eine Runde Seitenumbrüche.
Browse files Browse the repository at this point in the history
  • Loading branch information
mikesperber committed Aug 16, 2023
1 parent f8b9ca6 commit 357e230
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 76 deletions.
2 changes: 1 addition & 1 deletion i1akku.tex
Original file line number Diff line number Diff line change
Expand Up @@ -1557,7 +1557,7 @@ \section*{Aufgaben}
Programmiere das Newton-Verfahren!
Hinweis: Die Lösung ist fertig, wenn der Funktionswert nahe bei 0
Hinweis: Die Lösung ist fertig, wenn der Funktionswert nahe bei $0$
liegt, also kleiner als eine Toleranz ist. Da das Newton-Verfahren
nicht immer eine Lösung liefert, programmiere Deine Funktion so,
dass sie nach einer gewissen Anzahl von Schritten automatisch
Expand Down
26 changes: 14 additions & 12 deletions i1hop.tex
Original file line number Diff line number Diff line change
Expand Up @@ -1923,7 +1923,9 @@ \section*{Aufgaben}
einem Argument sowie eine Liste \lstinline{l} akzeptiert.
\lstinline{Filter-map} soll als Ergebnis die Liste der Rückgabewerte
von \lstinline{p} für diejenigen Elemente von \lstinline{l} zurückgeben,
für die \lstinline{p} nicht \lstinline{#f} zurückgibt. Beispiel:
für die \lstinline{p} nicht \lstinline{#f} zurückgibt.

Beispiel:

\begin{lstlisting}
(filter-map (lambda (x)
Expand All @@ -1939,17 +1941,19 @@ \section*{Aufgaben}
Verwende die Funktion \lstinline{list-fold} um folgende
Funktionen zu schreiben:
\begin{enumerate}
\item Schreibe eine Funktion \lstinline{list-map}, die eine Funktion auf jedes
Element einer Liste anwendet.
\item Schreibe eine neue Version der Funktion \lstinline{list-map},
die eine Funktion auf jedes Element einer Liste anwendet und die
Liste der Ergebnisse liefert.
\item Schreibe eine Funktion \lstinline{list-or}, die eine Funktion
auf alle Elemente einer Liste anwendet, die jeweils ein Boolean
liefert, und die Resultate mit \lstinline{or} verknüpft.
\item Schreibe eine Funktion \lstinline{count-trues}, die ein
Funktion mit boolescher Rückgabe auf
alle Elemente einer Liste anwendet und zählt, wie häufig \lstinline{#t}
zurückgegeben wird.
\item Schreibe eine Funktion \lstinline{contains?}, die feststellt, ob ein
Element in einer Liste enthalten ist.
\item Schreibe eine Funktion \lstinline{contains?}, die
\lstinline{#t} liefert, wenn ein
Element in einer Liste enthalten ist, sonst \lstinline{#f}.
\item Schreibe eine Funktion \lstinline{remove-duplicates}, die alle doppelten
Elemente aus einer Liste filtert.
\end{enumerate}
Expand Down Expand Up @@ -2094,10 +2098,7 @@ \section*{Aufgaben}
\end{aufgabe}

\begin{aufgabe}
Funktionen können nicht nur als Argumente an
andere Funktionen übergeben, sondern auch als Ergebnisse
zurückgeliefert werden. Dies soll in dieser Aufgabe genutzt werden,
um ein einfaches Telefonbuch zu programmieren: Ein Telefonbuch ist
Programmiere ein einfaches Telefonbuch: Ein Telefonbuch ist
als Funktion repräsentiert, die den Namen einer Person akzeptiert
und die Telefonnummer der Person zurückliefert.

Expand Down Expand Up @@ -2250,12 +2251,13 @@ \section*{Aufgaben}
(if (= n 0)
empty
(cons (stream-first stream)
(stream-take (- n 1)
((stream-rest-function stream)))))))
(stream-take (- n 1)
((stream-rest-function stream)))))))
\end{lstlisting}
%
(Dabei ist angenommen, dass die Selektoren
(Dabei haben wir angenommen, dass die Selektoren
\lstinline{stream-first} und \lstinline{stream-rest-function} heißen.)

\lstinline{Stream-take} lässt sich zum Beispiel auf das Ergebnis von
\lstinline{from} anwenden:
%
Expand Down
60 changes: 25 additions & 35 deletions i1lambda.tex
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ \section{Die Sprache des $\lambda$-Kalküls}
dann Sinn, wenn wir auch Funktionen herstellen können: Wir brauchen
also Abstraktionen. Und da zu jeder Abstraktion auch eine oder
mehrere Variablen gehören, brauchen wir auch Variablen. Drei
fundamentale Ideen also::
fundamentale Ideen also:
%
\begin{itemize}
\item Applikation
Expand Down Expand Up @@ -965,8 +965,8 @@ \section{Auswertungsstrategien}
%
Die Call-by-Name-Reduktion hört also verglichen mit der
Linksaußen-Reduktion vorzeitig auf, sobald eine Abstraktion
herauskommt. Beim Beispiel von oben also schon nach dem ersten
Schritt:
herauskommt. Beim Beispiel von oben ist schon nach dem
ersten Schritt:
%
\begin{eqnarray*}
\underline{(\lambda \lrm x.\lambda \lrm z.(\lambda \lrm y.\lrm y)~\lrm x)~((\lambda \lrm y.\lrm y)~\lrm z)}
Expand Down Expand Up @@ -995,7 +995,6 @@ \section{Auswertungsstrategien}
\end{array}
\end{displaymath}
%

Eine andere Auswertungsstrategie
vermeidet solche doppelte Arbeit: Die \textit{Linksinnen"=Reduktion},
auch genannt \emph{applicative-order reduction\index{applicative-order reduction}} oder
Expand All @@ -1018,28 +1017,27 @@ \section{Auswertungsstrategien}
\end{enumerate}
\end{definition}
%
Die Linksinnen-Reduktion ist beim obigen Beispiel effektiver, da
zunächst das Argument der äußeren Applikation ausgewertet wird:
Die Linksinnen-Reduktion ist beim obigen Beispiel effektiver als
Linksaußen-Reduktion,
da zunächst das Argument der äußeren Applikation ausgewertet wird:
%
\begin{displaymath}
\begin{array}{rl}
(\lambda \lrm x.\lrm x~\lrm x)~((\lambda \lrm y.\lrm y)~(\lambda \lrm z.\lrm z))
\rightarrow_{\beta i} &
(\lambda \lrm x.\lrm x~\lrm x)~(\lambda \lrm z.\lrm z)
\\
(\lambda \lrm x.\lrm x~\lrm x)~((\lambda \lrm y.\lrm y)~z)
\rightarrow_{\beta i} &
(\lambda \lrm z.\lrm z)~(\lambda \lrm z.\lrm z)
(\lambda \lrm x.\lrm x~\lrm x)~z
\\
\rightarrow_{\beta i} &
(\lambda \lrm z.\lrm z)
\lrm z~\lrm z
\end{array}
\end{displaymath}
%
Leider führt die Linksinnen-Reduktion nicht immer zu einer Normalform,
selbst wenn es die Linksaußen"=Reduktion tut. Der Term
selbst wenn es die Linksaußen"=Reduktion tut. Der folgende Term
zum Beispiel hat zwei Redexe~-- den ganzen Term und
$(\lambda \lrm z.\lrm z~\lrm z)~(\lambda \lrm z.\lrm z~\lrm z)$:
\[ (\lambda \lrm x.\lambda \lrm y.\lrm y)~((\lambda \lrm z.\lrm z~\lrm z)~(\lambda \lrm z.\lrm z~\lrm z)) \]
zum Beispiel hat zwei Redexe, einmal den ganzen Term und dann noch
$(\lambda \lrm z.\lrm z~\lrm z)~(\lambda \lrm z.\lrm z~\lrm z).$ Die Linksinnen-Strategie wählt
Die Linksinnen-Strategie wählt
den inneren Subterm als ersten Redex aus:
%
\begin{displaymath}
Expand All @@ -1055,17 +1053,16 @@ \section{Auswertungsstrategien}
\label{page:call-by-value}
Eine Variante der Linksinnen-Reduktion, die in den meisten
Programmiersprachen Anwendung findet, ist die
\textit{Call-by-Value-Reduktion\index{Call-by-Value-Reduktion}}:
\textit{Call-by-Value-Reduktion\index{Call-by-Value-Reduktion}}.
Sie ist die Grundlage
insbesondere für unsere Lehrsprachen.
%
\begin{definition}[Call-by-Value-Reduktion]\label{def:call-by-value}
Die \textit{Call-by-Value-Reduktion} ist wie die
Linksinnen-Reduktion, allerdings ohne die letzte Regel für Terme der
Form $\lambda v.e$.
\end{definition}
%
Die Call-by-Value-Reduktion ist die Grundlage
insbesondere für unsere Lehrspracher. Der Stepper macht das sichtbar.
%
\begin{aufgabeinline}
Lasse folgendes Programm (und gern auch noch andere) im Stepper
laufen und überzeuge Dich, dass er die Call-by-Value-Reduktion
Expand All @@ -1076,11 +1073,8 @@ \section{Auswertungsstrategien}
\end{lstlisting}
\end{aufgabeinline}
%
Programmiersprachen, die von innen nach außen auswerten, heißen
\textit{Call-by-Value-Sprachen\index{Call-by-Value-Sprache}} oder
\textit{strikten\index{strikte Sprache} Sprachen} zu sprechen.
Die meisten Programmiersprachen sind strikt.

Programmiersprachen mit Call-by-Value-Reduktion heißen
\textit{strikte\index{strikte Sprache} Sprachen}.
Es gibt auch \textit{nicht-strikte\index{nicht-strikte Sprache}}
Sprachen wie zum Beispiel Haskell~\cite{Haskell2010},
die auf der sogenannten \textit{lazy evaluation\index{lazy evaluation}} beruhen. Ihre
Expand Down Expand Up @@ -1209,7 +1203,7 @@ \subsection{Natürliche Zahlen}
&=& \lceil 2\rceil
\end{eqnarray*}
%
Folgende Definition berechnet das Gegenteil der Nachfolgerfunktion, die \textit{Vorgängerfunktion}.\index{Vorgänger}
Folgende Definition die \textit{Vorgängerfunktion}.\index{Vorgänger}
Sie zieht von einer Zahl eins ab:\index{Vorgänger}\index{pred@$\mathbf{pred}$}
%
\begin{displaymath}
Expand Down Expand Up @@ -1250,9 +1244,8 @@ \subsection{Rekursion und Fixpunktsatz}
Rekursion. Wenn wir in den Lehrsprachen rekursive Funktionen
geschrieben haben, dann war das immer im Zusammenhang mit
\lstinline{define}, damit die Funktion überhaupt einen Namen bekommt.
Das geht auch $\lambda$-Kalkül, ist aber etwas aufwendiger.

Zunächst einmal: Das einfache \lstinline{define} können wir in eine
Das einfache \lstinline{define} können wir in eine
Kombination aus Abstraktion und Applikation übersetzen. Aus einem
Programm dieser Form:
%
Expand Down Expand Up @@ -1290,11 +1283,9 @@ \subsection{Rekursion und Fixpunktsatz}
\lambda \lrm x.\mathbf{if}~(\mathbf{zerop}~\lrm x)~\lceil 1\rceil~(\ast~\lrm x~(?~(\mathbf{pred}~\lrm x)))
\end{displaymath}
%
Immerhin ist zu sehen, dass dieser Term korrekt die Fakultät von $0$
ausrechnet, nämlich $1$. Für alle Zahlen größer als $0$ ist es allerdings
schlecht bestellt, da der Term "<$?$"> noch unbekannt ist.
Weil der obige Term nur für $0$ taugt, sei er mit $\mathbf{fac}_0$
benannt:
Immerhin berechnet dieser Term korrekt die Fakultät von $0$, nämlich
$1$. Für alle Zahlen größer als $0$ ist "<$?$"> allerdings noch unbekannt.
Weil der Term nur für $0$ taugt, nennen wir ihn $\mathbf{fac}_0$:
%
\begin{displaymath}
\mathbf{fac}_0 \deq \lambda \lrm x.\mathbf{if}~(\mathbf{zerop}~\lrm
Expand Down Expand Up @@ -1356,12 +1347,11 @@ \subsection{Rekursion und Fixpunktsatz}
$\mathbf{FAC}$ ist also eine Fabrik für Fakultätsfunktionen und
teilt mit allen $\mathbf{fac}_i$ die Eigenschaft, dass ihre
Definition nicht rekursiv ist.

Damit ist zwar die Notation kompakter geworden,
eine korrekte
Definition von $\mathbf{fac}$ müsste aber eine unendliche
Kette von Applikationen von $\mathbf{FAC}$ enthalten.
Da sich ein Term mit einer unendlichen Kette von Applikationen nicht aufschreiben lässt, hilft im Moment nur Wunschdenken weiter.
Da sich solch ein Term nicht aufschreiben lässt, hilft nur Wunschdenken weiter.
Dafür sei angenommen, $\mathbf{fac}$ wäre bereits gefunden. Dann gilt folgende
Gleichung:
%
Expand Down Expand Up @@ -1481,7 +1471,7 @@ \subsection{Rekursion und Fixpunktsatz}

Es gibt immer noch Elemente von
Programmiersprachen, die wir in diesem Kapitel nicht in den
$\lambda$-Kalkül abgebildet haben, zum Beispiel globale Definitionen,
$\lambda$-Kalkül abgebildet haben, zum Beispiel
Zeichenketten oder
Signaturen. All dies ist aber prinzipiell möglich. Das macht
$\lambda$-Kalkül zu einem nützlichen Modell für die
Expand Down
2 changes: 1 addition & 1 deletion i1prop.tex
Original file line number Diff line number Diff line change
Expand Up @@ -1385,7 +1385,7 @@ \section{Suchbäume testen}
\end{lstlisting}
%
Die Funktion \lstinline{every?} haben wir extra für diesen Testfall
programmiert~-- strikt nach Konstruktionsanleitung für Listen:\label{func:everyp}
programmiert:\label{func:everyp}
%
\indexvariable{every?}
\begin{lstlisting}
Expand Down
53 changes: 26 additions & 27 deletions i1tree.tex
Original file line number Diff line number Diff line change
Expand Up @@ -1105,9 +1105,8 @@ \section{Suchbäume}
\end{lstlisting}
%
Wir schreiben nun eine Variante von \lstinline{tree-member?},
die Suchbäume statt "<normale"> Bäume
akzeptieren. Hier sind Kurzbeschreibung, Signatur und Tests für die
Funktion für das Suchen:\label{func:search-tree-member}
die Suchbäume
akzeptiert:\label{func:search-tree-member}
%
\begin{lstlisting}
; festellen, ob Element in Suchbaum vorhanden ist
Expand All @@ -1132,11 +1131,9 @@ \section{Suchbäume}
...))
\end{lstlisting}
%
Jetzt könnten wir natürlich alles wie immer ausprogrammieren. Viel
einfacher aber ist es, wenn wir die existierende Funktion
\lstinline{tree-member?} wiederverwenden: Wir haben ja genau das
gleiche vor, was die Funktion schon macht. Dazu kopieren wir sie
einfach in den Rumpf:
Für den Rumpf können wir die Funktion
\lstinline{tree-member?} wiederverwenden. Dazu kopieren wir sie
in die Definition von \lstinline{search-tree-member?}:
%
\begin{lstlisting}
(define search-tree-member?
Expand Down Expand Up @@ -1227,8 +1224,6 @@ \section{Suchbäume}
(tree-member? value (search-tree-tree search-tree))))
\end{lstlisting}
%
Fertig!

\begin{aufgabeinline}
Schreibe entsprechend zu \lstinline{search-tree-member?} eine
Funktion \lstinline{search-tree-insert} auf Basis von
Expand Down Expand Up @@ -1435,8 +1430,8 @@ \section{Suchbäume balancieren}
nicht nur dafür sorgen, dass das Suchen effizient funktioniert. Auch
das Balancieren sollte nicht zuviel Arbeit machen~-- sonst werden die
Vorteile beim Suchen durch den Aufwand beim Balancieren wieder
zunichte gemacht. Das bedeutet ganz praktisch, dass wir einen
Kompromiss suchen müssen zwischen den Einsparungen beim Suchen und dem
zunichte gemacht. Wir müssen einen
Kompromiss suchen zwischen den Einsparungen beim Suchen und dem
Aufwand für das Balancieren. Solche Kompromisse gibt es oft bei
Algorithmen, und oft werden Messungen eingesetzt, um sie auszuwählen
und schrittweise zu verbessern. So ist es auch hier.
Expand Down Expand Up @@ -1709,12 +1704,13 @@ \subsection{Balancierte Suchbäume}
(lambda (element)
(search-tree-of (sized-label-of element))))
\end{lstlisting}
\vspace*{-2ex}
\end{aufgabeinline}
%
Wir machen nun aus
\lstinline{search-tree-member?} die Funktion
Wir machen aus
\lstinline{search-tree-member?} nun
\lstinline{sized-search-tree-member?}. Dafür kopieren
wir sie, benennen sie um und stellen sie so um, dass sie
wir sie, benennen sie um und ändern sie, so dass sie
\lstinline{sized-search-tree} benutzt.\label{func:sized-search-tree-member}
%
\indexvariable{sized-search-tree-member?}
Expand Down Expand Up @@ -1748,7 +1744,7 @@ \subsection{Balancierte Suchbäume}
(tree-member? value (sized-search-tree-tree search-tree))))
\end{lstlisting}
%
Vergleiche sie mit \lstinline{search-tree-insert}: Es gibt nur zwei
Vergleiche sie mit \lstinline{search-tree-insert}. Es gibt nur zwei
Unterschiede:
%
\begin{itemize}
Expand All @@ -1758,9 +1754,9 @@ \subsection{Balancierte Suchbäume}
\lstinline{sized-node-label} auf.
\end{itemize}
%
Nun widmen wir uns der Konstruktion von balancierten Suchbäumen und
schreiben eine Funktion \lstinline{balanced-search-tree-insert}. Auch
sie entsteht aus \lstinline{search-tree-insert} durch Kopieren und
Nun widmen wir uns der Konstruktion von balancierten Suchbäumen.
Die Funktion dafür entsteht
entsteht aus \lstinline{search-tree-insert} durch
Umstellen auf \lstinline{sized-search-tree}:
%
\indexvariable{balanced-search-tree-insert}
Expand Down Expand Up @@ -2002,7 +1998,7 @@ \section{Ausbalancieren durch Rotation}
Abbildung~\ref{fig:search-tree-rotation} nicht, dass die Bäume auf
der rechten Seite nicht mehr sortiert sind?
\end{aufgabeinline}
%
Wann sollte der Algorithmus nun einfach und wann doppelt rotieren?
Schau Dir dazu noch einmal in Abbildung~\ref{fig:search-tree-rotation}
den oberen linken Baum an. Von den Teilbäumen $X$ und $Y$ ist einer
Expand All @@ -2025,7 +2021,7 @@ \section{Ausbalancieren durch Rotation}
%
Diese Rotationen übertragen wir jetzt in den Rumpf von
\lstinline{make-balanced-node}. Damit wir dabei möglichst keine
Fehler machen, verwenden wir Namen genau wie in
Fehler machen, verwenden wir die gleichen Namen im Code wie in
Abbildung~\ref{fig:search-tree-rotation}:
%
\begin{lstlisting}
Expand Down Expand Up @@ -2088,6 +2084,7 @@ \section{Ausbalancieren durch Rotation}
(define b (sized-node-label Y))
(define Y1 (node-left-branch Y))
(define Y2 (node-right-branch Y))
(make-sized-node b
(make-sized-node a X Y1)
(make-sized-node c Y2 Z))))
Expand All @@ -2103,7 +2100,9 @@ \section{Ausbalancieren durch Rotation}
gegenüber dem gerade behandelten Fall "<gespiegelt">. Auch der letzte
Fall fehlt noch: Der tritt dann ein, wenn der Baum kein exzessives
Ungleichgewicht aufweist. Dann könnten wir einfach
\lstinline{make-sized-node} benutzten. Hier die ganze Funktion:\label{func:make-balanced-node}
\lstinline{make-sized-node} benutzten.
Hier die ganze Funktion:\label{func:make-balanced-node}
%
\indexvariable{make-balanced-node}
\begin{lstlisting}
Expand Down Expand Up @@ -2153,19 +2152,19 @@ \section{Ausbalancieren durch Rotation}
(make-sized-node label left-branch right-branch)))))
\end{lstlisting}
%
Es gibt bisher noch keine Tests für die Funktionen
Für
\lstinline{balanced-search-tree-insert} und
\lstinline{make-balanced-node}. Das ist bedenklich, insbesondere weil
\lstinline{make-balanced-node} gibt es bisher noch keine Tests.
Das ist bedenklich, insbesondere weil
die Funktionen ziemlich kompliziert sind und viele Fälle abdecken
müssen. Es lohnt sich, ein weiteres Mal an
Mantra~\ref{mantra:komplexitaet} auf
Seite~\pageref{mantra:komplexitaet} zu erinnern:
\mantrakomplexitaet*
\noindent So richtig testen werden wir sie im nächsten Kapitel. Hier schon
einmal eine Aufgabe zur Vorbereitung:
\noindent So richtig testen werden wir sie im nächsten Kapitel. Hier
eine Aufgabe zur Vorbereitung:
\begin{aufgabeinline}
Mach Dir schomal ein paar Gedanken darüber, \emph{was} eigentlich
Expand Down

0 comments on commit 357e230

Please sign in to comment.