@@ -371,8 +371,8 @@ \section{Zwischenergebnisse mitführen}
371371
372372\section {Schablonen für Funktionen mit Akkumulator }
373373
374- Auch für Funktionen mit Akkumulator wollen wir eine
375- Konstruktionsanleitung entwickeln . Vorher wollen wir aber noch einmal
374+ Auch für Funktionen mit Akkumulator entwickeln wir eine
375+ Konstruktionsanleitung. Vorher wollen wir aber noch einmal
376376anhand eines weiteren Beispiels Revue passieren lassen, wie der
377377Konstruktionsprozess bei solchen Funktionen eigentlich genau aussieht.
378378
@@ -536,7 +536,7 @@ \section{Schablonen für Funktionen mit Akkumulator}
536536könnte das so aussehen:
537537%
538538\begin {center }
539- \lstinline {Sum } ist die Summer aller Elemente in \lstinline {list0} vor
539+ \lstinline {sum } ist die Summer aller Elemente in \lstinline {list0} vor
540540 \lstinline {list}.
541541\end {center }
542542%
@@ -565,7 +565,7 @@ \section{Schablonen für Funktionen mit Akkumulator}
565565%
566566\ begin{lstlisting}
567567(define list-sum-helper
568- ;; sum ist die Summer aller Elemente in list0 vor list
568+ ; sum ist die Summer aller Elemente in list0 vor list
569569 (lambda (list sum)
570570 (cond
571571 ((empty? list) sum)
@@ -581,8 +581,8 @@ \section{Schablonen für Funktionen mit Akkumulator}
581581%
582582\ begin{lstlisting}
583583(define list-sum-helper
584- ;; sum ist die Summer aller Elemente in list0 vor list
585- e (lambda (list sum)
584+ ; sum ist die Summer aller Elemente in list0 vor list
585+ (lambda (list sum)
586586 (cond
587587 ((empty? list) sum)
588588 ((cons? list)
@@ -600,7 +600,7 @@ \section{Schablonen für Funktionen mit Akkumulator}
600600(define list-sum
601601 (lambda (list0)
602602 (define list-sum-helper
603- ;; sum ist die Summer aller Elemente in list0 vor list
603+ ; sum ist die Summer aller Elemente in list0 vor list
604604 (lambda (list sum)
605605 (cond
606606 ((empty? list) sum)
@@ -617,7 +617,7 @@ \section{Schablonen für Funktionen mit Akkumulator}
617617(define list-sum
618618 (lambda (list0)
619619 (define accumulate
620- ;; sum ist die Summer aller Elemente in list0 vor list
620+ ; sum ist die Summer aller Elemente in list0 vor list
621621 (lambda (list sum)
622622 (cond
623623 ((empty? list) sum)
@@ -661,7 +661,7 @@ \section{Schablonen für Funktionen mit Akkumulator}
661661die Listen akzeptiert: Du musst eine Invariante finden, und dafür gibt
662662es nur wenig allgemeingültige Hilfestellung.
663663
664- \begin {aufgabeinline }
664+ \begin {aufgabeinline }\label { aufgabe:list-min-nonemepty-acc }
665665 Schreibe die Funktion \lstinline {list-min-nonempty} aus
666666 Abschnitt~\ref {sec:list-min-nonempty } auf
667667 Seite~\pageref {sec:list-min-nonempty } noch einmal, diesmal mit
@@ -751,7 +751,7 @@ \section{Über natürliche Zahlen akkumulieren}
751751(define power
752752 (lambda (base exponent0)
753753 (define accumulate
754- ;; power ist base^(exponent0 - exponent)
754+ ; power ist base^(exponent0 - exponent)
755755 (lambda (exponent power)
756756 (cond
757757 ((zero? exponent) ...)
@@ -775,7 +775,7 @@ \section{Über natürliche Zahlen akkumulieren}
775775(define power
776776 (lambda (base exponent0)
777777 (define accumulate
778- ;; power ist base^(exponent0 - exponent)
778+ ; power ist base^(exponent0 - exponent)
779779 (lambda (exponent power)
780780 (cond
781781 ((zero? exponent)
@@ -825,9 +825,192 @@ \section{Über natürliche Zahlen akkumulieren}
825825 Produkt \[ 1\times 2\times \cdots \times (n-1)\times n\] .
826826\end {aufgabeinline }
827827
828-
829828\section {Aktienkurse analysieren }
830829
830+ Es gibt durchaus Funktionen, bei denen mehrere Zwischenergebnisse
831+ nötig sind. Um das zu demonstrieren, schreiben wir eine Funktion, die
832+ den maximalen Gewinn aus einer Reihe von Aktienkursen berechnet.
833+
834+ Diese Reihe von aufeinanderfolgenden Kursen repräsentieren wir als
835+ Liste von Zahlen. Entsprechend sehen Kurzbeschreibung und Signatur so
836+ aus:
837+ %
838+ \ begin{lstlisting}
839+ ; Bestmöglichen Gewinn durch Kauf und Verkauf ermitteln
840+ (: max-profit ((nonempty-list-of real) -> real))
841+ \end {lstlisting }
842+ %
843+ Gewinn erzielt man hier, indem die Aktie zunächst gekauft und dann
844+ wieder verkauft wird~-- und nicht umgekehrt. Die Differenz zwischen
845+ Verkaufs- und Kaufpreis ist dann der Gewinn. Der folgende Testfall
846+ illustriert dies:
847+ %
848+ \ begin{lstlisting}
849+ (check-expect (max-profit (list 5 2 7 3 9 5 1)) 7)
850+ \end {lstlisting }
851+ %
852+ Hier hätte man den maximalen Gewinn erzielt durch Kauf zum Kurs 2 und
853+ Verkauf zum Kurs 9.
854+
855+ Das Gerüst sieht so aus:
856+ %
857+ \ begin{lstlisting}
858+ (define max-profit
859+ (lambda (spots)
860+ ...)))
861+ \end {lstlisting }
862+ %
863+ ("< Spot"> ist ein englisches Wort für "< Kurs"> .)
864+
865+ Aber wie geht es weiter?
866+
867+ Die einfache Strategie, einfach die Differenz zwischen Maximum und
868+ Minimum der Liste als maximalen Gewinn auszuweisen, funktioniert
869+ leider nicht: Das Minimum ist 1, liegt aber leider hinter dem Maximum
870+ von 9.
871+
872+ Ebensowenig eignet sich dieses Problem für eine naive, "< normale">
873+ rekursive Funktion. Die Schablone wäre diese hier:
874+ %
875+ \ begin{lstlisting}
876+ (define max-profit
877+ (lambda (spots)
878+ (cond
879+ ((empty? spots) ...)
880+ ((cons? spots)
881+ ... (first spots) ...
882+ ... (max-profit (rest spots) ...)))))
883+ \end {lstlisting }
884+ %
885+ Das Problem ist, dass der Profit des Rests der Liste nicht ausreicht,
886+ um daraus und dem ersten Element den Profit der Gesamtliste
887+ auszurechnen. Wir müssten dafür auch noch den dazugehörigen
888+ Verkaufskurs wissen.
889+
890+ Wir versuchen es also mal mit Zwischenergebnissen. Dafür sieht die
891+ Schablone so aus:
892+ %
893+ \ begin{lstlisting}
894+ (define max-profit
895+ (lambda (spots0)
896+ (define accumulate
897+ (lambda (spots acc)
898+ (cond
899+ ((empty? spots) acc)
900+ ((cons? spots)
901+ (accumulate (rest spots)
902+ ... (first spots) ... acc ...)))))
903+ (accumulate spots0 ...)))
904+ \end {lstlisting }
905+ %
906+ Da die Funktion im ganzen den maximalen Gewinn ausrechnen soll, bietet
907+ es sich an, als Zwischenergebnis den maximalen Gewinn der bisher
908+ gesehenen Kurse zwischen \lstinline {spots0} und \lstinline {spots} als
909+ Zwischenergebnis mitzuführen:
910+ %
911+ \ begin{lstlisting}
912+ (define max-profit
913+ (lambda (spots0)
914+ (define accumulate
915+ ; max-profit ist der maximale Gewinn zwischen
916+ ; spots0 und spots
917+ (lambda (spots max-profit)
918+ (cond
919+ ((empty? spots) max-profit)
920+ ((cons? spots)
921+ (accumulate (rest spots)
922+ ... (first spots) ... acc ...)))))
923+ (accumulate spots0 0)))
924+ \end {lstlisting }
925+ %
926+ Das dumme ist, dass wir auch hier nicht genug Daten haben, um beim
927+ rekursiven Aufruf von \lstinline {accumulate} einen neuen Wert für
928+ \lstinline {max-profit} zu berechnen. Grundsätzlich gibt es aber nur
929+ zwei Möglichkeiten:
930+ %
931+ \begin {itemize }
932+ \item \lstinline {Max-profit} bleibt, wie es ist.
933+ \item \lstinline {Max-profit} wird aktualisiert, weil es besser ist,
934+ zum Kurs \lstinline {(first spots)} zu verkaufen, als zum bisher
935+ besten Verkaufskurs.
936+ \end {itemize }
937+ %
938+ Wir müssen also herausbekommen, wie hoch der Gewinn wäre, wenn wir zum
939+ Kurs \lstinline {(first spots)} verkaufen würden. Das könnten wir,
940+ wenn wir den bestmöglichen \emph {Kaufkurs } wüssten. Etwas Überlegung
941+ ergibt, dass dieser Kaufkurs der minimale Kurs unter den vergangenen
942+ Kursen ist: Dieses Minimum führen wir als weiteres Zwischenergebnis
943+ mit. Aktualisiert wird dieses Minimum mit der eingebauten Funktion
944+ \lstinline {min}:
945+ %
946+ \ begin{lstlisting}
947+ (define max-profit
948+ (lambda (spots0)
949+ (define accumulate
950+ ; min-spot ist das Minimum der Elemente zwischen
951+ ; spots0 und spots
952+ ; max-profit ist der maximale Gewinn zwischen
953+ ; spots0 und spots
954+ (lambda (spots min-spot max-profit)
955+ (cond
956+ ((empty? spots) max-profit)
957+ ((cons? spots)
958+ (accumulate (rest spots)
959+ (min (first spots) min-spot)
960+ ...)))))
961+ (accumulate spots0 ... 0)))
962+ \end {lstlisting }
963+ %
964+ Es fehlt noch der richtige Anfangswert für \lstinline {min-spot} beim
965+ ersten Aufruf von \lstinline {accumulate}. Hier machen wir Gebrauch
966+ von der Tatsache, dass \lstinline {spots0} eine nicht-leere Liste ist:
967+ Wir können sie in erstes Element und Rest aufteilen wie schon bei
968+ \lstinline {list-min} in Abschnitt~\ref {sec:list-min-nonempty} auf
969+ Seite~\pageref {sec:list-min-nonempty } sowie in
970+ Aufgabe~\ref {aufgabe:list-min-nonemepty-acc } auf
971+ Seite~\pageref {aufgabe:list-min-nonemepty-acc }. Das erste Element ist
972+ dann das erste Minimum:
973+ %
974+ \ begin{lstlisting}
975+ (accumulate (rest spots0) (first spots0) 0)))
976+ \end {lstlisting }
977+ %
978+ Es fehlt nur noch der aktualisierte Wert für \lstinline {max-profit}:
979+ Der ist das Maximum aus dem bisherigen \lstinline {max-profit} und dem
980+ möglichen Gewinn aus dem Verkauf zum Kurs \lstinline {(first spots)}.
981+ Hier das Ergebnis:
982+ %
983+ \ begin{lstlisting}
984+ (define max-profit
985+ (lambda (spots0)
986+ (define accumulate
987+ ; min-spot ist das Minimum der Elemente zwischen
988+ ; spots0 und spots
989+ ; max-profit ist der maximale Gewinn zwischen
990+ ; spots0 und spots
991+ (lambda (spots min-spot max-profit)
992+ (cond
993+ ((empty? spots) max-profit)
994+ ((cons? spots)
995+ (accumulate (rest spots)
996+ (min (first spots) min-spot)
997+ (max (- (first spots) min-spot)
998+ max-profit))))))
999+ (accumulate (rest spots0) (first spots0) 0)))
1000+ \end {lstlisting }
1001+ %
1002+ Fertig!
1003+
1004+ \begin {aufgabeinline }\index {Fibonacci-Folge}
1005+ Die \textit {Fibonacci-Folge } ist eine Folge natürlicher Zahlen, die
1006+ mit 0 und 1 anfängt. Jede weitere Zahl ist die Summer der beiden
1007+ Zahlen davor.
1008+
1009+ Schreibe eine Funktion, welche für eine Zahl $ n$ die $ n$ -te Zahl aus
1010+ der Fibonacci-Zahl berechnet. Schreibe dafür eine Funktion
1011+ mit zwei Akkumulatoren.
1012+ \end {aufgabeinline }
1013+
8311014\section {Kontext und Endrekursion }
8321015\label {sec:iteration }
8331016
0 commit comments