Vinzenz Mai: Fehler in Rekursion

Beitrag lesen

Hallo Hendrik,

Du hast Dir viel Mühe gegeben und eine entsprechend ausführliche Antwort verdient.

Nehmen wir nun diesen String als Ausgang so übergeben wir:
build_ltrarray(0,1,"A8H2IQ");

Denn der letzte Aufruf dürfte in etwa so aussehen:
build_ltrarray(ord("Q"),ord("R")+1,"Q");

Nur folgende Zeile if ($pos_ltr_now === false) ergibt false = false, weil
$pos_ltr_now für die Position von Q in "Q" steht welche 0 ist.

Dies wird von PHP so interpretiert als ob 0 Boolean wäre und somit false.

nein. Deswegen

  

>     $pos_ltr_nxt  = [link:http://de2.php.net/manual/de/function.strpos.php@title=strpos]($sig2,$letter2+65);  

      //...  

>     if ($pos_ltr_nxt === false or $pos_ltr_now == $pos_ltr_nxt)  
> 		$this->build_ltrarray($letter,$letter2+1,$sig2);  

verwendest Du ja den strikten Vergleich mit ===.

0 === false => false. Der Grund für den Nichtabbruch liegt irgendwo anders, allerdings hast Du Dir noch viele weitere Fallstricke eingebaut, an denen Deine Methode scheiterte (den schlechten Rat der Sackkarre mit eingeschlossen, aber dazu später mehr).

Nehmen wir uns den nächsten Abschnitt vor:

    // Aufruf mit build_ltrarray(0, 8, "A8H2IQ")  
  
    $sig2 = "A8H2IQ";  
    $letter = 0;  
    $pos_ltr_now = 0;  
    $letter2 = 8;        // entspricht I;  
    $pos_ltr_nxt = 4;  
    $bletter[$letter] = 0;  
  
    // Kontrollausgabe  
    echo $sig2, "<br>\n";  
  
    //Anzahl Buchstabe in Array einfügen  
    switch ($pos_ltr_nxt-$pos_ltr_now)  
        {  
        case 1:  
        //Buchstabe nur einmal vorhanden  
        $barray[$letter] = $barray[$letter]+1;  
        break;  
        default:  
        //Buchstabe x mal vorhanden  
        $barray[$letter] = $barray[$letter]+substr($sig2,$pos_ltr_now+1,$pos_ltr_nxt-1);  
  
        // Kontrollausgaben  
        echo substr($sig2, $pos_ltr_now + 1, $pos_ltr_nxt - 1), "<br>\n";  
        echo $barray[$letter], "<br>\n";  
        break;  
  
        }  
    //$sig2 kürzen  
    $sig2 = substr($sig2,$pos_ltr_nxt,strlen($sig2)-$pos_ltr_nxt);  
  
    // Kontrollausgabe  
    echo $sig2;  

Die Ausgabe sieht wie folgt aus:

A8H2IQ
8H2
8
IQ

Oha: die Anzahl von A stimmt, aber H wird komplett unterschlagen :-( Nicht gut.

Aber noch lustiger wird es mit einer minimal veränderten Signatur:

$sig2 = "A8E2IQ";

Die Ausgabe lautet nun:

A8E2IQ
8E2
800
IQ

Jetzt stimmt noch nicht einmal die Anzahl von A, weil 8E2 zu 800 ausgewertet wird :-)

Spätestens jetzt solltest Du merken, dass die Logik Deiner Funktion zu wünschen übrig lässt.

Jetzt komme ich zu dem Problem, das die Sackkarre bereits angesprochen hat:
Selbst wenn Du Deine Rekursion sauber beendest, lässt Du PHP fleißig arbeiten, aber ohne zählbares Ergebnis. Das ist ein Problem der Gültigkeit und Lebensdauer von Variablen. Ich will es Dir an einer einfachen rekursiven Funktion aufzeigen. Eine Anmerkung noch: Da Du $this verwendest, gehe ich davon aus, dass es sich um eine Methode einer Klasse handelt:

class demo {  
    function beispiel_scalar($tiefe) {  
        // Ist die Variable noch nicht gesetzt, so weise ihr den Wert 3 zu  
        if (!isset($wert)) {  
            $wert = 3;  
        }  
	else {  
            // sonst erhöhe sie um 1  
            // $wert += 1  
            // ist übersichtlicher als  
            // $wert = $wert + 1  
            $wert += 1;  
        }  
        // Gib die erreichte Tiefe und den aktuellen Wert aus  
	echo "Tiefe: ", $tiefe, " - Wert: ", $wert, "<br>\n";  
  
        // Falls wir noch nicht ganz unten angelangt sind (in Tiefe 3)  
        if ($tiefe < 3) {  
            // gehen wir eine Etage tiefer  
            $this->beispiel_scalar($tiefe + 1);  
        }  
    }  
}  
  
$a = new demo();  
// Wir gehen vom Erdgeschoss (Tiefe 0) nach unten  
$a->beispiel_scalar(0);  

// Ausgabe:
Tiefe: 0 - Wert: 3
Tiefe: 1 - Wert: 3
Tiefe: 2 - Wert: 3
Tiefe: 3 - Wert: 3

Da ich hier wie Du auch nur lokale Variablen verwendet habe, ist bei jedem neuen Aufruf von beispiel_scalar() die Variable $wert zu Beginn neu erzeugt, sie ist nicht gesetzt und es wird jedesmal der if-Zweig abgearbeitet.

Eine Möglichkeit, solche Werte über die Ausführung der Funktion zu erhalten, ist es, diese als Eigenschaft der Klasse zu deklarieren und anschließend zu nutzen:

class demo2 {  
  
    $wert = NULL;  
    // isset(null) => false  
  
    function beispiel_scalar($tiefe) {  
        // Ist die Variable noch nicht gesetzt, so weise ihr den Wert 3 zu  
        // Auf eine Objekteigenschaft greifen wir mit $this zu:  
        if (!isset($this->wert)) {  
            $this->wert = 3;  
        }  
	else {  
            // sonst erhöhe sie um 1  
            $this->wert += 1;  
        }  
        // Gib die erreichte Tiefe und den aktuellen Wert aus  
	echo "Tiefe: ", $tiefe, " - Wert: ", $this->wert, "<br>\n";  
  
        // Falls wir noch nicht ganz unten angelangt sind (in Tiefe 3)  
        if ($tiefe < 3) {  
            // gehen wir eine Etage tiefer  
            $this->beispiel_scalar($tiefe + 1);  
        }  
    }  
}  
  
$a = new demo2();  
// Wir gehen vom Erdgeschoss (Tiefe 0) nach unten  
$a->beispiel_scalar(0);  

Ausgabe:
Tiefe: 0 - Wert: 3
Tiefe: 1 - Wert: 4
Tiefe: 2 - Wert: 5
Tiefe: 3 - Wert: 6

Beim ersten Aufruf ergibt isset($this->wert) false, die Variable wird gesetzt, in den weiteren Aufrufen wird der else-Zweig abgearbeitet, weil die Eigenschaft jetzt gesetzt ist.

Aber Vorsicht: Ein weiterer Aurfuf der Methode läßt weiterzählen:
Kopiere einfach die Zeile

$a->beispiel_scalar(0);  

so dass die Methode zweimal aufgerufen wird, lasse das Skript lauten und die Ausgabe lautet:

Tiefe: 0 - Wert: 3
Tiefe: 1 - Wert: 4
Tiefe: 2 - Wert: 5
Tiefe: 3 - Wert: 6
Tiefe: 0 - Wert: 7
Tiefe: 1 - Wert: 8
Tiefe: 2 - Wert: 9
Tiefe: 3 - Wert: 10

Vergleichbar gilt, dass bei einem zweiten Aufruf Deiner build_ltrarray-Methode die neuen Werte zu den alten dazuaddiert werden. Wahrscheinlich wird es sinnvoller sein, wenn Deine Methode das gewünschte Array zurückgibt.

Falls Du meine Ausführungen verstanden hast, könnten wir an einen neuen Ansatz für Deine Funktion gehen. Falls Du meine Ausführungen nicht ganz verstanden hast oder nicht nachvollziehen kannst, erläutere bitte, wo Du Verständnisschwierigkeiten hast oder hängengeblieben bist, damit wir es Dir besser erklären können.

Freundliche Grüße

Vinzenz