Warum rekursiv den Platz effizient machen?

Ich fand diese Aussage beim Studium der Funktionalen Reaktiven Programmierung von"Ein Weltraumleck mit einem Pfeil verschließen" von Hai Liu und Paul Hudak (Seite 5):

Suppose we wish to define a function that repeats its argument indefinitely:

    repeat x = x : repeat x

or, in lambdas:

    repeat = λx → x : repeat x

This requires O(n) space. But we can achieve O(1) space by writing instead:

    repeat = λx → let xs = x : xs
                  in xs

Der Unterschied hier scheint gering zu sein, aber er wirkt sich sehr positiv auf die Raumeffizienz aus. Warum und wie passiert es? Die beste Vermutung, die ich gemacht habe, ist, sie von Hand zu bewerten:

    r = \x -> x: r x
    r 3

    -> 3: r 3 
    -> 3: 3: 3: ........
    -> [3,3,3,......]

Wie oben müssen wir für diese Rekursion unendlich viele neue Thunks erstellen. Dann versuche ich den zweiten zu bewerten:

    r = \x -> let xs = x:xs in xs
    r 3

    -> let xs = 3:xs in xs
    -> xs, according to the definition above: 
    -> 3:xs, where xs = 3:xs
    -> 3:xs:xs, where xs = 3:xs

In der zweiten Form diexs erscheint und kann von jedem Ort geteilt werden, an dem es vorkommt. Deshalb können wir wohl nur verlangenO(1) Leerzeichen stattO(n). Aber ich bin mir nicht sicher, ob ich Recht habe oder nicht.

Übrigens: Das Schlüsselwort "shared" stammt von Seite 4 des gleichen Papiers:

Das Problem hierbei ist, dass die standardmäßigen Call-by-Need-Bewertungsregeln nicht erkennen können, dass die Funktion:

f = λdt → integralC (1 + dt) (f dt) 

ist das gleiche wie:

f = λdt → let x = integralC (1 + dt) x in x

Die erstere Definition bewirkt, dass die Arbeit im rekursiven Aufruf von f wiederholt wird, während im letzteren Fall die Berechnung geteilt wird.

Antworten auf die Frage(3)

Ihre Antwort auf die Frage