Unerklärliche Probleme mit rekursiver Funktion

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Unerklärliche Probleme mit rekursiver Funktion

    Hallo,

    ich habe zurzeit ein Problem mit einer rekursiven PHP Funktion die Infos von URLs auslesen soll. Die Funktion an sich habe ich leider gerade nicht zur Verfügung, da diese auf dem Server meines Arbeitgebers zur Entwicklung ruht.
    Aber mein Problem ist auch recht simpel zu verstehen.

    Hier ein kleines Beispiel

    PHP-Code:
    <?php
    // Hier der Funktionskopf Parameter insgesamt 9 aber Hier nur 3 zur Anschauung
    function GetUrlInfo($url,$cnt_sites,$result_array) {
      
    // Seiten Zählen und Ausgeben
      
    $cnt_sites++;
      echo 
    $cnt_sites.'<br />';
      
    // Informationen holen und in Array speichern (hier nur Beispielhaft)
      
    $infos = array('Information1',
                     
    'Information2');
      
    // Ergebnisarray füllen
      
    foreach($infos AS $info){ 
        
    // Werte als Key speichern
        
    $result_array[$info] = '';
      }
      
    print_r($result_array);
      echo 
    '<br />';
      
    // ca. 390 Zeilen Code
        //ein rekursiver Aufruf (von 5 Stück in der Funktion)
        
    GetUrlInfo($url,$cnt_sites,$result_array);
    }

    // Funktionsaufruf
    $result_array = array();
    $cnt_sites 0;
    $url 'www.example.com';

    GetUrlInfo($url,$cnt_sites,$result_array);

    ?>
    Mein Problem ist, dass bei mehrmaligem rekursiven Aufrufen der Funktion Werte des result Arrays erst gefüllt und dann um einen Wert geleert werden und später wieder gefüllt, wichtig ich Fasse das Array während der Funktion nicht mehr an.

    Außerdem zählt die $cnt_sites variable erst hoch und wird dann wieder um eins erniedrigt bis in den negativen Zahlenbereich und fängt dann irgendwann wieder an hochzuzählen, hier gilt wieder ich fasse die Variable bis zur Übergabe nicht mehr an.

    Können diese Probleme an der Speicherzuweisung liegen, sodass zum Beispiel zu wenig Arbeitsspeicher zur Verfügung steht (Arbeitspeicherzugriff Erweitern) oder ist die Funktion zu lang ca. 400 Zeilen Code oder sind es zuviele Parameter (9 Stück) ?????
    Ich bin völlig ratlos, da sich die zwei genannten Fehler nicht logisch erklären lassen.

    Bitte helft mir!

  • #2
    Hallo,

    deine Funktion hat kein return-Statement, das was da zwischendrin passiert hoch- und wieder runterzählen u.s.w. dürftest du nur im Debugger sehen, aber obwohl dort derselbe Variablenname angezeigt wird, siehst du in der Tat verschiedene Variablen, weil die alle in einem anderen Scope existieren.

    Du müsstest also entweder global verwenden (nicht empfohlen), call-by-reference (nicht empfohlen) oder das Ergebnis-Array mit return zurückgeben und beim rekursiven Aufruf der Funktion mit dessen Rückgabewert mergen.

    Gruß,

    Anja
    [COLOR="DarkSlateGray"]Hast du die [COLOR="DarkSlateGray"]Grundlagen zur Fehlersuche[/color] gelesen? Hast du Code-Tags benutzt?
    Hast du als URL oder Domain-Beispiele example.com, example.net oder example.org benutzt?
    Super, danke!
    [/COLOR]

    Kommentar


    • #3
      Ich weiß jetzt nicht genau wie das gemeint war.

      Aber erstmal vorweg, die Funktion hat mehrere return Statements und gibt am Ende auch das Ergebnissarray zurück wenn alle Bedingungen erfüllt sind bzw. teilweise erfüllt sind ansonsten lasse ich FALSE zurückgeben.
      Habe es aber aus Anschauungsgründen weggelassen also in etwa so:

      PHP-Code:
      // Hier der Funktionskopf Parameter insgesamt 9 aber Hier nur 3 zur Anschauung
      function GetUrlInfo($url,$cnt_sites,$result_array) {
        
      // Seiten Zählen und Ausgeben
        
      $cnt_sites++;
        echo 
      $cnt_sites.'<br />';
        
      // Wenn mehr als 200 Unterseiten aufgerufen wurden Funktion verlassen
        
      if($cnt_sites 200) {
          if(
      count($result_array) > 0){
            return(
      $result_array);
          } else {
            
      retrun(FALSE);
          }
        }
        
      // Informationen holen und in Array speichern (hier nur Beispielhaft)
        
      $infos = array('Information1',
                       
      'Information2');
        
      // Ergebnisarray füllen
        
      foreach($infos AS $info){ 
          
      // Werte als Key speichern
          
      $result_array[$info] = '';
        }
        
      print_r($result_array);
        echo 
      '<br />';
        
      // ca. 390 Zeilen Code
        
      if(noch neue Seiten vorhanden) {
          
      //ein rekursiver Aufruf (von 5 Stück in der Funktion) 
          // 
          // Wie soll ich den Rückgabewert behandeln?????
          
      $Rueckgabe GetUrlInfo($url,$cnt_sites,$result_array);

        } else {
          
      // HIER nochmal Beispiel für Rückgabe
          
      if(count($result_array) > 0){
            return(
      $result_array);
          } else {
            
      retrun(FALSE);
          }
      }

      // Funktionsaufruf
      $result_array = array();
      $cnt_sites 0;
      $url 'www.example.com';

      $result GetUrlInfo($url,$cnt_sites,$result_array);
      if(
      $result != FALSE){
        
      print_r($result);

      Kann ich denn mit dem Rückgabewert inerhalb der Funktion noch was anfangen? Normalerweise werden doch alle weiteren Zeilen ab dem rekursiven Funktionsaufruf ignoriert. Ich kann mir irgendwie nicht vorstellen wie dass aussehen soll.

      Kommentar


      • #4
        Zitat von subabrain Beitrag anzeigen
        Ich weiß jetzt nicht genau wie das gemeint war.

        Aber erstmal vorweg, die Funktion hat mehrere return Statements und gibt am Ende auch das Ergebnissarray zurück wenn alle Bedingungen erfüllt sind bzw. teilweise erfüllt sind ansonsten lasse ich FALSE zurückgeben.
        Habe es aber aus Anschauungsgründen weggelassen also in etwa so:

        Kann ich denn mit dem Rückgabewert inerhalb der Funktion noch was anfangen? Normalerweise werden doch alle weiteren Zeilen ab dem rekursiven Funktionsaufruf ignoriert. Ich kann mir irgendwie nicht vorstellen wie dass aussehen soll.
        Wer sagt denn sowas? Natürlich kann man nach dem rekursiven Aufruf noch Code unterbringen und der wird selbstverständlich auch interpretiert. Nicht jede Rekursion ist endständig. Dein Ergebnis-Array wird bei der rekursiven Übergabe an die Funktion (lazy) kopiert und die Aufrufende Funktion bekommt Änderungen daran logischerweise nicht mit (es sei denn, du übergibst es per Referenz). Dein Rückgabewert von false macht es in dem Fall aber nicht einfacher, es sei denn es ist beabsichtigt, dass der Hauptaufruf false zurückgibt, wenn einer der rekursiven (Sub(Sub(Sub...)))Aufrufe false zurückgibt.

        Es sieht leider so aus, als ob du dich noch etwas mehr mit dem Prinzip der Rekursion an sich auseinandersetzen müsstest.
        [COLOR="DarkSlateGray"]Hast du die [COLOR="DarkSlateGray"]Grundlagen zur Fehlersuche[/color] gelesen? Hast du Code-Tags benutzt?
        Hast du als URL oder Domain-Beispiele example.com, example.net oder example.org benutzt?
        Super, danke!
        [/COLOR]

        Kommentar


        • #5
          Naja,

          wenn ich von der Materie der Rekursion viel Ahnung hätte würde ich hier ja auch keine Fragen stellen (eine hilfreichere Antwort mit bischen Code wäre nicht schlecht gewesen) aber ich kann aus der Antwort wenigstens ableiten dass es die Möglichkeit von nicht endständigen Rekursionen gibt. Ich frage mich nur warum davon für PHP so wenig im Web zu finden ist? Wäre mal nicht schlecht wenn es irgendwo ausführliche Beispiele oder Erklärungen in PHP auch über die nicht endständigen Rekursionen gäbe. Denn wie schon gesagt habe zwar gesucht aber so gut wie nix gefunden (für PHP). Wenn also jemand n Link oder sowas hätte würde ich mich freuen.

          Danke trotzdem für die Antworten

          Kommentar


          • #6
            Rekursion mit Arrays

            Hier mal ein Grundgerüst, wie man es in so einem Fall immer benutzen kann:

            PHP-Code:
                function scanForFiles ($pPath) {
                    
            $result = array();
                    
                    
            // hier wird gescannt und das array gefüllt
                    
                    
            return $result;
                } 
            Also array erzeugen, irgendwas damit machen und zum Schluss zurückgeben.

            Dieser einfache Scanner taucht rekursiv in Verzeichnisse ab und sammelt alle Dateinamen unterhalb des Startpfades:

            PHP-Code:
                function scanForFiles ($pPath) {
                    
            $result = array();
                    
                    
            $di = new DirectoryIterator($pPath);
                    foreach (
            $di as $f) {
                        if (
            $f->isFile()) {
                            
            $result[] = $f->getPathname();
                        }
                        else if (
            $f->isDir() && !$f->isDot()) {
                            
            $temp scanForFiles($f->getPathname());
                            
            $result array_merge($result$temp);
                        }
                    }
                    
                    return 
            $result;
                } 
            Im nichtrekursiven Fall einer Datei wird deren Name in das Array eingefügt.

            Wenn es ein Verzeichnis ist (und dabei nicht . oder ..), wird die Funktion damit aufgerufen und liefert dessen Dateinamenliste zurück. Dieses Ergebnis wird als temp gespeichert. Um die bisher schon gesammelten Dateien im selben Verzeichnis mit den rekursiv gefundenen Dateien aus Unterverzeichnissen zu kombinieren, wird das temp-Resultat mittels array_merge mit dem result-Array zusammengefügt.

            Die Abbruchbedingung dieser Rekursion ist das Nichtvorhandensein von weiteren Unterverzeichnissen und wird irgendwann immer erfüllt, solange keine rekursiven Links im Dateisystem vorhanden sind.

            War das jetzt etwas verständlicher?
            [COLOR="DarkSlateGray"]Hast du die [COLOR="DarkSlateGray"]Grundlagen zur Fehlersuche[/color] gelesen? Hast du Code-Tags benutzt?
            Hast du als URL oder Domain-Beispiele example.com, example.net oder example.org benutzt?
            Super, danke!
            [/COLOR]

            Kommentar


            • #7
              DirectoryIterator
              Um Verzeichnisse rekursiv zu durchlaufen wurde der RecursiveItetatorIterator in Verbindung mit dem RecursiveDirectoryIterator erfunden.

              Da ist keine Handarbeit mehr nötig.

              Ich frage mich nur warum davon für PHP so wenig im Web zu finden ist?
              Es sind erstaunlich weing Leute überhaupt in der Lage recursive Problem zu erkennen und und dann auch noch zu bearbeiten.
              Zuletzt geändert von combie; 24.07.2009, 22:38.
              Wir werden alle sterben

              Kommentar


              • #8
                Zitat von combie Beitrag anzeigen
                Um Verzeichnisse rekursiv zu durchlaufen wurde der RecursiveItetatorIterator in Verbindung mit dem RecursiveDirectoryIterator erfunden.
                Danke, aber das weiß ich doch. Rat mal, warum ich den gerade NICHT genommen hab

                Ich wollte halt ein einfaches, aber trotzdem realitätsnahes Beispiel haben und da fiel mir nichts besseren ein, als Verzeichnisse durchzuwühlen.
                [COLOR="DarkSlateGray"]Hast du die [COLOR="DarkSlateGray"]Grundlagen zur Fehlersuche[/color] gelesen? Hast du Code-Tags benutzt?
                Hast du als URL oder Domain-Beispiele example.com, example.net oder example.org benutzt?
                Super, danke!
                [/COLOR]

                Kommentar


                • #9
                  Rat mal, warum ich den gerade NICHT genommen hab
                  KA!
                  Völliger Bahnhof.

                  Wenn, dann auch richtig.
                  Wir werden alle sterben

                  Kommentar


                  • #10
                    @combie

                    Hast du den Thread verfolgt? Es ging um rekursive Funktionen mit Arrays. Mit dem RecursiveIteratorIterator hätte ich kein rekursives Beispiel gehabt.

                    Gruß,

                    Anja
                    [COLOR="DarkSlateGray"]Hast du die [COLOR="DarkSlateGray"]Grundlagen zur Fehlersuche[/color] gelesen? Hast du Code-Tags benutzt?
                    Hast du als URL oder Domain-Beispiele example.com, example.net oder example.org benutzt?
                    Super, danke!
                    [/COLOR]

                    Kommentar


                    • #11
                      Nunja, aber geben tuts ihn doch: SPL-StandardPHPLibrary: RecursiveArrayIterator Class Reference
                      Wir werden alle sterben

                      Kommentar


                      • #12
                        Ja, den tut es geben, das weiß ich schon. Bitte nimm doch einfach zur Kenntnis, dass mir deine Einwände vertraut sind und ich mich bewusst gegen die rekursiven Iteratoren entschieden habe, um ein Beispiel für eine eigene rekursive Funktion mit Array-Rückgabe anzuführen, weil genau das die Frage oder das Problem von subabrain war.
                        [COLOR="DarkSlateGray"]Hast du die [COLOR="DarkSlateGray"]Grundlagen zur Fehlersuche[/color] gelesen? Hast du Code-Tags benutzt?
                        Hast du als URL oder Domain-Beispiele example.com, example.net oder example.org benutzt?
                        Super, danke!
                        [/COLOR]

                        Kommentar


                        • #13
                          @AmicaNoctis

                          Vielen Dank für diese Beispiele, hat mir sehr weitergeholfen.
                          Habe jetzt die Logik verstanden, die ich verstehen wollte.
                          Ich werde das dann so mit dem mergen in meine Funktion integrieren.
                          Nochmals vielen Dank für die Antwort.




                          @combie
                          Ich muss AmicaNoctis Recht geben Sie hat das richtige Beispiel gebracht. Die von dir angeführte Klasse oder was das ist würde meine Frage nicht beantworten sondern nur Mittel zum Zweck sein und außerdem will ich diese Klasse nicht verwenden.
                          Mir ist es lieber wenn ich die Zusammenhänge direkt verstehen kann als das nicht Verstehen durch eine Klasse oder Funktion zu ersetzen.
                          Zuletzt geändert von subabrain; 25.07.2009, 09:40.

                          Kommentar


                          • #14
                            Rekursion bietet einem so manches mal einen eleganten Lösungsansatz, ja es gibt sogar Situationen wo sie unvermeidbar sein kann.

                            Was Performance betrifft ist sie jedoch selten von Vorteil.

                            Kommt es auf Performance an, sollte man ermitteln, was eine rekursive Abarbeitung kostet und was eine iterative und dann entscheiden.

                            In sehr vielen Fällen wird man da diese Perfomancenachteile bei Rekursion erkennen.

                            Liegt eine rekursive Funktion in einem nicht weiter entscheidenden Bereich, d.h. Performance spielt keine Rolle, kann man sich die Arbeit sparen und das nehmen was man möchte.

                            Das nur mal als kleinen Denkanstoss.

                            Kommentar

                            Lädt...
                            X