Rekursive Generatoren in PHP

Einführung

Seit Version 5.5 in PHP gibt es so etwas Tolles wieGeneratoren. Ich werde die offizielle Handbuchseite nicht wiederholen, aber sie eignen sich hervorragend für die kurze Definition von Iteratoren. Das bekannteste Beispiel ist:

function xrange($from, $till, $step)
{
   if ($from>$till || $step<=0)
   {
      throw new InvalidArgumentException('Invalid range initializers');
   }

   for ($i = $from; $i < $till; $i += $step)
   {
      yield $i;
   }
}

//...

foreach (xrange(2, 13, 3) as $i)
{
   echo($i.PHP_EOL); // 2,5,8,11
}

und generator ist eigentlich keine Funktion, sondern eine Instanz einer konkreten Klasse:

get_class(xrange(1, 10, 1)); // Generator


Das Problem

Fertig mit RTM-Dingen, nun zu meiner Frage. Stellen Sie sich vor, wir möchten einen Generator für erstellenFibonacci-Zahlen. Um diese zu erhalten, können wir normalerweise die einfache Funktion verwenden:

function fibonacci($n)
{
   if(!is_int($n) || $n<0)
   {
      throw new InvalidArgumentException('Invalid sequence limit');
   }
   return $n < 2 ? $n : fibonacci($n-1) + fibonacci($n-2);
}

var_dump(fibonacci(6)); // 8

Lassen Sie uns dies in etwas verwandeln, das giltSequenz und nicht nur das letzte Mitglied:

function fibonacci($n)
{
   if (!is_int($n) || $n<0)
   {
      throw new InvalidArgumentException('Invalid sequence limit');
   }
   if ($n<2)
   {
      return range(0, $n);
   }
   $n1 = fibonacci($n-1);
   $n2 = fibonacci($n-2);
   return array_merge($n1, [array_pop($n1)+array_pop($n2)]);
}

//...

foreach (fibonacci(6) as $i)
{
   echo($i.PHP_EOL); // 0,1,1,2,3,5,8
}

Wir haben jetzt eine Funktion, die ein Array mit voller Sequenz zurückgibt


Die Frage

Zum Schluss noch die Frage: Wie kann ich meine neueste verwandelnfibonacci funktionieren so wird esAusbeute meine Werte, die nicht in einem Array enthalten sind? Meine$n kann groß sein, also möchte ich die Vorteile von Generatoren nutzen, wie inxrange Probe. Pseudocode ist:

function fibonacci($n)
{
   if (!is_int($n) || $n<0)
   {
      throw new InvalidArgumentException('Invalid sequence limit');
   }

   if ($n<2)
   {
      yield $n;
   }

   yield fibonacci($n-2) + fibonacci($n-1);
}

Aber das ist natürlich Mist, da wir nicht so damit umgehen können, weil Rekursion ein Klassenobjekt verursachtGenerator und nichtint Wert.

Bonus: Abrufen der Fibonacci-Sequenz ist nur ein Beispiel für eine allgemeinere Frage: Wie werden Generatoren mit Rekursion im allgemeinen Fall verwendet? Natürlich kann ich Standard verwendenIterator dafür oder schreiben Sie meine Funktion um, um eine Rekursion zu vermeiden. Aber ich möchte das mit Generatoren erreichen. Ist das möglich? Lohnt es sich, dies so zu nutzen?

Antworten auf die Frage(8)

Ihre Antwort auf die Frage