Grundlagen der funktionsorientierten Programmierung mit SCHEME/Echte und endständige Rekursionen

Aus ProgrammingWiki

Wiki-Quelltext dieser Seite.

<language>Scheme</language>
== Rekursionsarten ==

<table border="1" cellspacing="1" cellpadding="4">
<tr>
<th width="500">Echte Rekursion</th>
<th width="500">Endständige Rekursion</th>
</tr>
<tr>
<td valign="top">
Eine Prozedur heißt ''echt rekursiv'', wenn das Ergebnis erst im ''rekursiven Aufstieg'' ermittelt wird.<br />
Dazu müssen alle aktuellen Wertbindungen vor jedem neuen rekursiven Aufruf gespeichert werden (Stapel / Stack),
damit sie im rekursiven Aufstieg zur Verfügung stehen.
</td>
<td valign="top">
Eine Prozedur heißt ''endständig rekursiv'', wenn das Ergebnis bereits nach dem ''rekursiven Abstieg'' feststeht.<br />
Häufig wird das aktuelle Ergebnis in einer zusätzlichen Akkumulatorvariablen mitgeführt.
Die aufwändige Stapelverwaltung entfällt.<br />
Endständige Rekursionen bezeichnet man deshalb auch als ''Iterationen''.
</td>
</tr>
<tr>
<td valign="top">

<table border="0" cellspacing="0" cellpadding="0">
<tr><td>'''Beispiel:'''</td></tr>
<tr>
<td width="500" height="180" valign="top">
<code id="4c97d4c0a508c">
(define fak-rek
  (lambda (n)
    (if (= n 0)
        1
        (* n (fak-rek (- n 1))))))
</code>
</td>
</tr>
<tr><td>'''Trace-Modus:'''</td></tr>
<tr>
<td width="500" height="100" valign="top">
<code id="4c9e0cd390903" hidden>
(import debugging)
</code>
<run id="4c9e0cd390d96">
(trace 'fak-rek)
(fak-rek 5)
</run>
<code id="4ca253a986689" hidden>
(import debugging) 
(define fak-rek
  (lambda (n)
    (if (= n 0)
        1
        (* n (fak-rek (- n 1))))))
(trace 'fak-rek) (untrace 'fak-rek)
</code>
</td>
</tr>
</table>

</td>
<td valign="top">

<table border="0" cellspacing="0" cellpadding="0">
<tr><td>'''Beispiel:'''</td></tr>
<tr>
<td width="500" height="180" valign="top">
<code id="4c97d4c0a51aa">
(define fak-help
  (lambda (n fak)
    (if (= n 0)
        fak
        (fak-help (- n 1) (* n fak)))))

(define fak-it
  (lambda (n)
    (fak-help n 1)))
</code>
</td>
</tr>
<tr><td>'''Trace-Modus:'''</td></tr>
<tr>
<td width="500" height="100" valign="top">
<code id="4c9e0cd390b7c" hidden>
(import debugging)
</code>
<run id="4c9e0cd390ebb">
(trace 'fak-it) (trace 'fak-help) 
(fak-it 5)
</run>
<code id="4ca253a9867c1" hidden>
(import debugging) 
(define fak-help
  (lambda (n fak)
    (if (= n 0)
        fak
        (fak-help (- n 1) (* n fak)))))
(define fak-it
  (lambda (n)
    (fak-help n 1)))
(trace 'fak-it) (trace 'fak-help) 
(untrace 'fak-it) (untrace 'fak-help)
</code>
</td>
</tr>
</table>

</td>
<tr>
<td valign="top">
'''Trichtermodell der internen Abläufe:'''<br />
<center>[[Bild:Echte_Rekursion1.gif]]</center>
</td>
<td valign="top">
'''Trichtermodell der internen Abläufe:'''<br />
<center>[[Bild:Endständige_Rekursion1.gif]]</center>
</td>
</tr>
</table>

'''Beachte:''' Die mitunter effizientere endständige Rekursion darf zu keinen Zwängen bei der rekursiven Beschreibung von Problemlösungen führen!

== Aufgaben ==

<code id="4c9be4304018c" hidden>
(define sqr
  (lambda (x)
    (* x x)))
</code>

<ol>
<li>
=== Quadratzahlen ===

Eine Liste mit <math>n</math> Quadratzahlen soll generiert werden.<br />
* Geben Sie eine echt rekursive Lösung an.

<code id="4c9be430402be">
(define quadzahlen-rek
  (lambda (n)
    ...))
</code>

<check>(or (equal? (quadzahlen-rek 6) '(36 25 16 9 4 1)) (equal? (quadzahlen-rek 6) '(36 25 16 9 4 1 0)))</check>

<run id="4c9be4304065d">
(quadzahlen-rek 10)
</run>

* Implementieren Sie nun eine endständig rekursive Prozedur.

<code id="4c9be430403ed">
(define quadzahlen-help
  (lambda (n ls)
    ...))

(define quadzahlen-it
  (lambda (n)
    ...))
</code>

<check>(or (equal? (quadzahlen-it 6) '(1 4 9 16 25 36)) (equal? (quadzahlen-it 6) '(0 1 4 9 16 25 36)))</check>

<run id="4c9be43040742">
(quadzahlen-it 10)
</run>
</li>
<li>

<language>Scheme</language>
=== Turm von Hanoi ===

<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td valign="top">
Vermutlich stammt dieses Spiel von dem französischen Mathematiker '''Édouard Lucas''' (* 4. April 1842; † 3. Oktober 1891), bei dem ein Turm aus einzelnen Scheiben von <math>A</math> nach <math>B</math> unter Nutzung des Hilfsplatzes <math>H</math> umgesetzt werden soll. Dabei darf immer nur eine Scheibe so bewegt werden, dass eine kleinere auf einer größeren Scheibe liegt.<br />

'''Lucas''' dachte sich dazu die Geschichte aus, dass indische Mönche im großen Tempel zu Benares, im Mittelpunkt der Welt, einen Turm aus 64 goldenen Scheiben versetzen müssten.<br />
Wenn ihnen das gelungen sei, wäre das Ende der Welt gekommen.
</td>
<td valign="top">
[[Bild:Tower_of_Hanoi.jpg|miniatur|hochkant=1.5|Turm von Hanoi]]
</td>
</tr>
</table>
Gegeben sei folgende Implementation:

<code>
(define count 0)

(define verschiebe
  (lambda (n start h ziel)
    (if (> n 0)
        (begin
          (verschiebe (- n 1) start ziel h)
          (set! count (+ count 1))
          (display (string-append "Scheibe " (number->string n) ": " start " --> " ziel)) (newline)
          (verschiebe (- n 1) h start ziel)))))

(define hanoi
  (lambda (n)    
    (verschiebe n "A" "H" "B")
    (display (string-append "--> " (number->string count) " Bewegung(en)")) (newline)
    'ok))
</code>

'''Hinweis:''' Testen Sie die Prozedur mit kleinen Argumenten!

<run id="4c9be43040866">
(hanoi 3)
</run>

[[Bild:Tower_of_Hanoi.gif|frame|Turm von Hanoi mit 3 Scheiben]]

* Beschreiben Sie die Spielstrategie (d.h. den [[Grundlagen der funktionsorientierten Programmierung mit SCHEME/Der Algorithmusbegriff|Lösungsalgorithmus]]) verbal.
* Entscheiden Sie, ob eine echt rekursive oder endständig rekursive Prozedur vorliegt.
* Ermitteln Sie, welcher Zusammenhang zwischen der Anzahl der Scheiben und der Anzahl der erforderlichen Bewegungen besteht.
</li>
</ol>