<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://demowiki.knowlus.com/index.php?action=history&amp;feed=atom&amp;title=Endrekursion</id>
	<title>Endrekursion - Versionsgeschichte</title>
	<link rel="self" type="application/atom+xml" href="https://demowiki.knowlus.com/index.php?action=history&amp;feed=atom&amp;title=Endrekursion"/>
	<link rel="alternate" type="text/html" href="https://demowiki.knowlus.com/index.php?title=Endrekursion&amp;action=history"/>
	<updated>2026-05-15T01:28:12Z</updated>
	<subtitle>Versionsgeschichte dieser Seite in Demo Wiki</subtitle>
	<generator>MediaWiki 1.44.2</generator>
	<entry>
		<id>https://demowiki.knowlus.com/index.php?title=Endrekursion&amp;diff=13779&amp;oldid=prev</id>
		<title>imported&gt;KlartextJan: /* Einzelnachweise Links*/</title>
		<link rel="alternate" type="text/html" href="https://demowiki.knowlus.com/index.php?title=Endrekursion&amp;diff=13779&amp;oldid=prev"/>
		<updated>2024-03-07T14:56:29Z</updated>

		<summary type="html">&lt;p&gt;&lt;span class=&quot;autocomment&quot;&gt;Einzelnachweise Links&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Neue Seite&lt;/b&gt;&lt;/p&gt;&lt;div&gt;Eine [[Rekursion|rekursive]] [[Funktion (Programmierung)|Funktion]] &amp;#039;&amp;#039;f&amp;#039;&amp;#039; ist &amp;#039;&amp;#039;&amp;#039;endrekursiv&amp;#039;&amp;#039;&amp;#039; ({{enS|&amp;#039;&amp;#039;tail recursive&amp;#039;&amp;#039;}}; auch &amp;#039;&amp;#039;&amp;#039;endständig rekursiv&amp;#039;&amp;#039;&amp;#039;, &amp;#039;&amp;#039;&amp;#039;iterativ rekursiv&amp;#039;&amp;#039;&amp;#039;, &amp;#039;&amp;#039;&amp;#039;repetitiv rekursiv&amp;#039;&amp;#039;&amp;#039;), wenn der rekursive Funktionsaufruf die letzte Aktion zur Berechnung von &amp;#039;&amp;#039;f&amp;#039;&amp;#039; ist.&amp;lt;ref name=&amp;quot;sicp&amp;quot;&amp;gt;Harold Abelson, Gerald Jay Sussman, sowie Julie Sussman: {{Webarchiv |url=http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-11.html#%_sec_1.2.1 |text=&amp;#039;&amp;#039;Linear Recursion and Iteration&amp;#039;&amp;#039; |wayback=20060903155101 |archiv-bot=}}. In: &amp;#039;&amp;#039;[[Structure and Interpretation of Computer Programs]]&amp;#039;&amp;#039;. Second edition, The MIT Press 1996, ISBN 0-262-51087-1&amp;lt;/ref&amp;gt; Vorteil dieser Funktionsdefinition ist, dass kein zusätzlicher Speicherplatz zur Verwaltung der Rekursion benötigt wird.&lt;br /&gt;
&lt;br /&gt;
== Automatisches Entfernen von endständigen Funktionsaufrufen ==&lt;br /&gt;
Bei der naiven Abarbeitung einer rekursiven Funktion steigt der [[Arbeitsspeicher|Speicherplatzverbrauch]] linear mit der Rekursionstiefe, da bei jedem Funktionsaufruf Speicherplatz für das Aufzeichnen der aktuellen [[Continuation]] des Programmflusses und der [[Funktionsparameter]] belegt wird (etwa zum Sichern der [[Rücksprungadresse]] und des aktuellen &amp;#039;&amp;#039;stack frame&amp;#039;&amp;#039; auf dem [[Aufrufstack]]). Außerdem kann während der Abarbeitung der aufgerufenen Funktion weiterer Speicherplatz für das Ablegen von funktionslokalen Variablen belegt werden. Bei einem endständigen Funktionsaufruf werden die im für die aufrufende Funktion belegten Speicherbereich abgelegten Werte aber nur noch für die Parameterübergabe an die endständig aufgerufene Funktion benötigt, so dass dieser Speicherbereich wiederverwendet werden kann. Somit können endrekursive Funktionen automatisch (etwa im Rahmen eines Optimierungsschrittes des [[Compiler]]s) in [[iterative Funktion]]en umgeformt werden, deren Speicherplatzverbrauch bei der Abarbeitung unabhängig von der Rekursionstiefe ist. Bei der Umformung werden die Aufrufe der endständigen Funktion durch entsprechende [[Sprunganweisung]]en ersetzt (&amp;#039;&amp;#039;tail call elimination&amp;#039;&amp;#039;).&lt;br /&gt;
&lt;br /&gt;
Einige Programmiersprachen wie etwa [[Scheme]] verlangen die automatische Umformung von endrekursiven Funktionen in iterative Funktionen als Teil ihrer Sprachdefinition. Andere Programmiersprachen wie etwa [[C (Programmiersprache)|C]], [[C++]] und [[C-Sharp|C#]] oder [[Java (Programmiersprache)|Java]] verlangen diese Umformung nicht, lassen sie aber für die jeweilige Sprachimplementierung als Optimierung zu. Als Optimierung ist diese Technik häufig in Compilern für [[funktionale Programmiersprache]]n zu finden, da bei Verwendung eines funktionalen Programmierstils die rekursive/endrekursive Formulierung für viele Algorithmen besonders häufig ist und darum solchen Formulierungen im Rahmen der Programmoptimierung beim Übersetzen durch einen Compiler besondere Beachtung zukommt.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ersetzen von Funktionsaufrufen durch Sprunganweisungen mit Wiederverwendung des aktuellen &amp;#039;&amp;#039;stack frame&amp;#039;&amp;#039; erschwert die [[Ablaufverfolgung]] eines Programms bei der [[Debugging|Fehleranalyse]], da der Aufrufstack beim Unterbrechen eines laufenden Programms an einem [[Haltepunkt (Programmierung)|Haltepunkt]] die Aufrufreihenfolge der Funktionen nicht vollständig wiedergibt.&lt;br /&gt;
&lt;br /&gt;
== Explizite Endrekursion ==&lt;br /&gt;
Die Programmiersprache [[Clojure]] bietet den expliziten Aufruf &amp;lt;code&amp;gt;recur&amp;lt;/code&amp;gt; für Endrekursion. Dies hat den Vorteil, dass der Compiler es erkennt, wenn der Aufruf nicht aus der Endposition erfolgt, und den Programmierer darauf hinweist.&amp;lt;ref&amp;gt;{{Internetquelle|url=https://clojuredocs.org/clojure.core/recur|titel=recur - clojure.core {{!}} ClojureDocs - Community-Powered Clojure Documentation and Examples|werk=clojuredocs.org|zugriff=2017-01-18}}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendbarkeit und Verallgemeinerung ==&lt;br /&gt;
Die Anwendbarkeit der Technik zur Ersetzung von endständigen Funktionsaufrufen durch Sprünge ist nicht auf endrekursive Funktionen beschränkt. Scheme verlangt beispielsweise auch über Funktionsgrenzen hinweg die Ausführung von endständigen Funktionen mit konstantem Speicherplatzverbrauch (&amp;#039;&amp;#039;proper tail recursion&amp;#039;&amp;#039;), beispielsweise für zwei Funktionen, die einander endständig aufrufen.&amp;lt;ref name=&amp;quot;r5rs&amp;quot;&amp;gt;{{cite journal&lt;br /&gt;
| author = Richard Kelsey, William Clinger, Jonathan Rees et al.&lt;br /&gt;
| month = August&lt;br /&gt;
| year = 1998&lt;br /&gt;
| title = Revised&amp;lt;sup&amp;gt;5&amp;lt;/sup&amp;gt; Report on the Algorithmic Language Scheme&lt;br /&gt;
| url = https://schemers.org/Documents/Standards/R5RS/&lt;br /&gt;
| journal = Higher-Order and Symbolic Computation&lt;br /&gt;
| volume = 11&lt;br /&gt;
| issue = 1&lt;br /&gt;
| pages = 7–105&lt;br /&gt;
| doi = 10.1023/A:1010051815785&lt;br /&gt;
}}&amp;lt;/ref&amp;gt;&amp;lt;ref name=&amp;quot;Clinger1998&amp;quot;&amp;gt;William Clinger: {{Webarchiv |url=http://www.cesura17.net/~will/Professional/Research/Papers/tail.pdf |text=&amp;#039;&amp;#039;Proper tail recursion and space efficiency&amp;#039;&amp;#039; |wayback=20151030113036 |archiv-bot=}} (PDF; 240&amp;amp;nbsp;kB), Proceedings of the 1998 ACM Conference on Programming Language Design and Implementation, Juni 1998, S. 174–185&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch den Übergang zu [[Continuation-passing style]] lassen sich prinzipiell Programme so umformen, dass alle Funktionsaufrufe durch endständige Aufrufe ersetzt werden. Dazu müssen jedoch alle aufgerufenen Funktionen so umgeformt werden, dass sie eine [[Continuation]] als Parameter übernehmen, die sie dann explizit mit Übergabe des Funktionsergebnisses zur Ausführung des weiteren Programmlaufs endständig aktivieren. Bei der Ausführung eines solchermaßen umgeformten Programms wird dann konstanter Speicherplatz für die Ablage der &amp;#039;&amp;#039;activation records&amp;#039;&amp;#039; (etwa auf dem Aufrufstack) benötigt, aber der für die Ablage der Fortsetzungen benötigte Speicherplatz ist nicht beschränkt. Als Folge dieser Umformung ist dann die mögliche Rekursionstiefe einer Routine durch den zur Ablage der Fortsetzungen verfügbaren Speicherplatz beschränkt statt durch die Größe des Aufrufstacks.&amp;lt;ref name=&amp;quot;eopl&amp;quot;&amp;gt;Daniel P. Friedman, Mitchell Wand, Christopher T. Haynes: &amp;#039;&amp;#039;Essentials of Programming Languages&amp;#039;&amp;#039;. Second edition, MIT Press 2001, ISBN 0262062178&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
Gegeben sei die rekursive Funktion &amp;#039;&amp;#039;sum&amp;#039;&amp;#039;, die die Summe der ersten &amp;#039;&amp;#039;n&amp;#039;&amp;#039; [[natürliche Zahl|natürlichen Zahlen]] berechnet:&lt;br /&gt;
  sum(n)&lt;br /&gt;
    if n=0&lt;br /&gt;
      return 0&lt;br /&gt;
    else&lt;br /&gt;
      return n + sum(n-1)&lt;br /&gt;
&lt;br /&gt;
Da nicht der rekursive Funktionsaufruf, sondern die Addition die letzte Aktion bildet, handelt es sich nicht um eine endrekursive Funktion. Die Berechnung von &amp;lt;code&amp;gt;sum(3)&amp;lt;/code&amp;gt; würde damit folgende Schritte beinhalten:&lt;br /&gt;
&lt;br /&gt;
 sum(3) = 3 + sum(2)&lt;br /&gt;
  sum(2) = 2 + sum(1)&lt;br /&gt;
   sum(1) = 1 + sum(0)&lt;br /&gt;
    sum(0) = 0&lt;br /&gt;
   sum(1) = 1 + 0 = 1&lt;br /&gt;
  sum(2) = 2 + 1 = 3&lt;br /&gt;
 sum(3) = 3 + 3 = 6&lt;br /&gt;
&lt;br /&gt;
In diesem Fall ist jedoch eine Umformung in eine endrekursive Darstellung möglich.&lt;br /&gt;
  sum(n)&lt;br /&gt;
    return add_sum (0, n)&lt;br /&gt;
&lt;br /&gt;
  add_sum(m, n)&lt;br /&gt;
    if n=0&lt;br /&gt;
      return m&lt;br /&gt;
    else&lt;br /&gt;
      return add_sum (m+n, n-1)&lt;br /&gt;
&lt;br /&gt;
Die endrekursive Hilfsfunktion &amp;lt;code&amp;gt;add_sum&amp;lt;/code&amp;gt; empfängt zwei Parameter &amp;lt;code&amp;gt;m&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; und liefert als Ergebnis die Summe aus &amp;lt;code&amp;gt;m&amp;lt;/code&amp;gt; und der Summe der ersten &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; natürlichen Zahlen. Der Aufruf &amp;lt;code&amp;gt;add_sum (0, n)&amp;lt;/code&amp;gt; liefert somit das gewünschte Ergebnis, die Summe der ersten &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; natürlichen Zahlen. Während des Ablaufs der Endrekursion in &amp;lt;code&amp;gt;add_sum&amp;lt;/code&amp;gt; werden die Zwischenergebnisse im &amp;lt;code&amp;gt;m&amp;lt;/code&amp;gt;-Parameter akkumuliert. In dieser endrekursiven Formulierung würde die Berechnung von &amp;lt;code&amp;gt;sum(3)&amp;lt;/code&amp;gt; etwa folgende Schritte beinhalten:&lt;br /&gt;
&lt;br /&gt;
 sum(3) = add_sum(0, 3)&lt;br /&gt;
        = add_sum(3, 2)&lt;br /&gt;
        = add_sum(5, 1)&lt;br /&gt;
        = add_sum(6, 0)&lt;br /&gt;
        = 6&lt;br /&gt;
&lt;br /&gt;
Bei der Umformung wurde implizit das [[Assoziativgesetz]] für die Addition natürlicher Zahlen ausgenutzt. Die ursprüngliche Definition von &amp;lt;code&amp;gt;sum(n)&amp;lt;/code&amp;gt; berechnete &amp;lt;code&amp;gt;sum(3)&amp;lt;/code&amp;gt; als&lt;br /&gt;
 3 + (2 + (1 + 0))&lt;br /&gt;
Die umgeformte Formulierung berechnet denselben Wert als&lt;br /&gt;
 ((0 + 3) + 2) + 1&lt;br /&gt;
&lt;br /&gt;
Wie jede [[Primitiv-rekursive Funktion|primitiv rekursive]] Funktion lässt sich die Endrekursion mittels [[Iteration]] darstellen.&lt;br /&gt;
  sum(n)&lt;br /&gt;
     m := 0&lt;br /&gt;
&lt;br /&gt;
     while (n &amp;gt; 0) do&lt;br /&gt;
       m   := m + n&lt;br /&gt;
       n   := n - 1&lt;br /&gt;
     end-while&lt;br /&gt;
     return m&lt;br /&gt;
&lt;br /&gt;
Rekursive wie iterative Lösungen stellen meist eine direkte Umsetzung eines Problems dar, welches schrittweise analysiert wurde. Platzersparnis und Lesbarkeit gehen dabei auf Kosten der Ausführungszeit. Vielfach lohnt daher die Suche nach effizienteren Algorithmen. So ist der beste Algorithmus zur Berechnung des Beispielfalles vor allem durch die „[[Gaußsche Summenformel|Gaußsche Schulgeschichte]]“ bekannt geworden:&lt;br /&gt;
  sum(n) = (n*(n+1)) / 2&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für Endrekursion mit mehreren beteiligten Funktionen hier zwei Funktionen &amp;lt;code&amp;gt;even&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;odd&amp;lt;/code&amp;gt; zur Feststellung, ob eine gegebene natürliche Zahl gerade oder ungerade ist.&lt;br /&gt;
  even(n)&lt;br /&gt;
    if n=0&lt;br /&gt;
      return true&lt;br /&gt;
    else&lt;br /&gt;
      return odd(n-1)&lt;br /&gt;
&lt;br /&gt;
  odd(n)&lt;br /&gt;
    if n=0&lt;br /&gt;
      return false&lt;br /&gt;
    else&lt;br /&gt;
      return even(n-1)&lt;br /&gt;
Die beiden Funktionen rufen sich gegenseitig endständig auf. Für sich genommen ist keine der beiden Funktionen endrekursiv.&lt;br /&gt;
&lt;br /&gt;
== Verallgemeinerung ==&lt;br /&gt;
Allgemein ist eine Funktion &amp;#039;&amp;#039;f&amp;#039;&amp;#039; endrekursiv, wenn sie sich auf folgende Weise definieren lässt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f(x) = \begin{cases} s(x), &amp;amp; \mbox{falls }R(x)\\f(r(x)), &amp;amp; \mbox{sonst} \\  \end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei sind &amp;#039;&amp;#039;r&amp;#039;&amp;#039; und &amp;#039;&amp;#039;s&amp;#039;&amp;#039; beliebige nicht mittels &amp;#039;&amp;#039;f&amp;#039;&amp;#039; definierte&lt;br /&gt;
Funktionen und &amp;#039;&amp;#039;R&amp;#039;&amp;#039; die Abbruchbedingung.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[Platzkomplexität]]&lt;br /&gt;
&lt;br /&gt;
== Einzelnachweise ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Theoretische Informatik]]&lt;br /&gt;
[[pt:Recursividade (ciência da computação)#Funções recursivas em cauda]]&lt;/div&gt;</summary>
		<author><name>imported&gt;KlartextJan</name></author>
	</entry>
</feed>