Markus: rekursion... Perl verweigert den Dienst...

ich habe eine script zum erzeugen einer Baumstruktur geschrieben...
das funktioniert auch super, bis zur 16. rekursion, ist das in Perl irgenwo festgelegt, wie oft eine subroutine verschachtelt aufgerufen werden darf?

Wäre schön wenn irgendjemand was zu dem Thema sagen könnte!
  mFG Markus

  1. hi markus!

    nein, perl schränkt hier meines wissens nach in keiner weise ein.

    die frage ist ja, ob du deine routine wirklich 16x in sich selbst verschachteln musst?

    ein stück code ist hier wohl unabdingbar...

    1. hi markus!

      nein, perl schränkt hier meines wissens nach in keiner weise ein.

      die frage ist ja, ob du deine routine wirklich 16x in sich selbst verschachteln musst?

      ein stück code ist hier wohl unabdingbar...

      OK
      Die Datenbanktstruktur sieht wie folgt aus:
      +-------+--------+-------+--------+
      | ID    | name   | layer | parent |
      +-------+--------+-------+--------+
      |   1   | Home   |     0 |      1 |
      |   2   | test1  |     1 |      1 |
          .       .        .        .
          .       .        .        .
          .       .        .        .
      | 38086 | test2  |     1 |      1 |
      | 38087 | blabla |     2 |      1 |
      +-------+--------+-------+--------+

      das script wie folgt:

      sub checkchild {
        my $tree;
        my @vars = sql(qq{
         SELECT * FROM ebay_categories
         WHERE parent = '$_[0]' AND ID != parent});
        my $count = (@vars/4);
        if ($count > 0)
         {
         for (my $c = 0; $c < $count; $c++)
          {
          my $layer = "";
          while ($vars[2+$c*4] >= 0)
           {
           $layer.="&nbsp;";
           $vars[2+$c*4]--;
           if ($vars[2+$c*4] == 0)
            {
            $layer.="&raquo;";
            }
           }
          $tree.="$layer<a href="#">$vars[1+$c*4]</a><br>\n";
          $tree.=checkchild($vars[0+$c*4]);
          }
         }
        return($tree);
        }

      print checkchild('1');

      vielleicht sieht ja nun jemand, was ich 'falsch' mache...

      Gruß, Markus

      1. Da fehlt eine maskierung \ vor dem " beim link <a href="#">,
        die bitte dazu denken!

        P.S.: das ganze läuft auch gut mit tabellen die nicht grade 38000 Datensätze enthalten, sondern nur 50 oder so...

        mfG Markus

      2. Die Datenbanktstruktur sieht wie folgt aus:
        +-------+--------+-------+--------+
        | ID    | name   | layer | parent |
        +-------+--------+-------+--------+
        |   1   | Home   |     0 |      1 |
        |   2   | test1  |     1 |      1 |
            .       .        .        .
            .       .        .        .
            .       .        .        .
        | 38086 | test2  |     1 |      1 |
        | 38087 | blabla |     2 |      1 |
        +-------+--------+-------+--------+

        das script wie folgt:

        sub checkchild {
          my $tree;
          my @vars = sql(qq{
           SELECT * FROM ebay_categories
           WHERE parent = '$_[0]' AND ID != parent});

        Für mich sieht es so aus, als ob di hier alle Datensätzemit der parent ID $_[0] abfragst.

        my $count = (@vars/4);

        Also hier die Anzahl der Datnesätze durch 4.

        if ($count > 0)

        $count ist immer größer null, solange in @vars ein Wert steht, es ist wohl sinnvoller direkt auf @vars oder $vars[0] zuz prüfen.

        {
           for (my $c = 0; $c < $count; $c++)
            {
            my $layer = "";
            while ($vars[2+$c*4] >= 0)

        und hier hört'es auf. Plötzlich ist $vars[..] kein Datensatz sondern ein Wert ungleich null, was ergibt denn oben deine sql Abfrage? Nicht die Datensätze?

        {
             $layer.="&nbsp;";
             $vars[2+$c*4]--;
             if ($vars[2+$c*4] == 0)

        Mir scheint es, du willst irgendwelche einfachen Zusmamenhänge in deiner DB ergründen, diese machst du aber extrem Umständlich und Zeitintensiv (Du überträgst wahrscheinlich die komplette DB mit allen 38.000 Einträgen von deinem DB server)

        {
              $layer.="&raquo;";
              }
             }
            $tree.="$layer<a href="#">$vars[1+$c*4]</a><br>\n";

        Warum du einen Link benutzt ohne eine URL ist natürlich auch nocht ein Rätsel.

        $tree.=checkchild($vars[0+$c*4]);
            }
           }
          return($tree);
          }

        print checkchild('1');

        vielleicht sieht ja nun jemand, was ich 'falsch' mache...

        so ganz verstehe ich nicht was du wirklich machen willst, da es nicht ersichtlich ist, was in @vars steht. Unter Umständen ist das ganze viel einfacher über eine sinnvolle SQL Abfrage zu lösen.

        Struppi.

        1. Die Datenbanktstruktur sieht wie folgt aus:
          +-------+--------+-------+--------+
          | ID    | name   | layer | parent |
          +-------+--------+-------+--------+
          |   1   | Home   |     0 |      1 |
          |   2   | test1  |     1 |      1 |
              .       .        .        .
              .       .        .        .
              .       .        .        .
          | 38086 | test2  |     1 |      1 |
          | 38087 | blabla |     2 |      1 |
          +-------+--------+-------+--------+

          das script wie folgt:

          sub checkchild {
            my $tree;
            my @vars = sql(qq{
             SELECT * FROM ebay_categories
             WHERE parent = '$_[0]' AND ID != parent});

          Für mich sieht es so aus, als ob di hier alle Datensätzemit der parent ID $_[0] abfragst.

          Ganz genau! außer dem der selbst parent ist...

          my $count = (@vars/4);

          Also hier die Anzahl der Datnesätze durch 4.

          Das bringt die &sql funktion so mit, die liefert ein array der ergebnismenge, also pro datensatz 4 einträge im array, /4 = anzahl der datensätze

          if ($count > 0)

          $count ist immer größer null, solange in @vars ein Wert steht, es ist wohl sinnvoller direkt auf @vars oder $vars[0] zuz prüfen.

          ok, kann man machen, geb ich dir recht

          {
             for (my $c = 0; $c < $count; $c++)
              {
              my $layer = "";
              while ($vars[2+$c*4] >= 0)

          und hier hört'es auf. Plötzlich ist $vars[..] kein Datensatz sondern ein Wert ungleich null, was ergibt denn oben deine sql Abfrage? Nicht die Datensätze?

          Vergiss die while schleife, die malt nur entsprechend dem Layer, bzw. der Eintragstiefe leerzeichen und ein &raquo; in den skalar $layer

          {
               $layer.="&nbsp;";
               $vars[2+$c*4]--;
               if ($vars[2+$c*4] == 0)

          Mir scheint es, du willst irgendwelche einfachen Zusmamenhänge in deiner DB ergründen, diese machst du aber extrem Umständlich und Zeitintensiv (Du überträgst wahrscheinlich die komplette DB mit allen 38.000 Einträgen von deinem DB server)

          {
                $layer.="&raquo;";
                }
               }
              $tree.="$layer<a href="#">$vars[1+$c*4]</a><br>\n";

          Warum du einen Link benutzt ohne eine URL ist natürlich auch nocht ein Rätsel.

          weil ich den link hier nich veröffentlichen wollte hab ich ihn durch # ersetzt

          $tree.=checkchild($vars[0+$c*4]);
              }
             }
            return($tree);
            }

          print checkchild('1');

          vielleicht sieht ja nun jemand, was ich 'falsch' mache...

          so ganz verstehe ich nicht was du wirklich machen willst, da es nicht ersichtlich ist, was in @vars steht. Unter Umständen ist das ganze viel einfacher über eine sinnvolle SQL Abfrage zu lösen.

          jetzt besser?

          Struppi.

          Markus

          1. sub checkchild {
              my $tree;
              my @vars = sql(qq{
               SELECT * FROM ebay_categories
               WHERE parent = '$_[0]' AND ID != parent});

            Für mich sieht es so aus, als ob di hier alle Datensätzemit der parent ID $_[0] abfragst.
            Ganz genau! außer dem der selbst parent ist...

            und dazu brauchst du eine Rekursive Funktion?

            Ist das nicht das Gleiche wie:

            my @vars = sql(qq{
            SELECT * FROM ebay_categories
            WHERE ID != parent SORT BY parent});

            my $count = (@vars/4);

            Also hier die Anzahl der Datnesätze durch 4.
            Das bringt die &sql funktion so mit, die liefert ein array der ergebnismenge, also pro datensatz 4 einträge im array, /4 = anzahl der datensätze

            Benutzt du nicht das DBI Modul?

            Struppi.

            1. sub checkchild {
                my $tree;
                my @vars = sql(qq{
                 SELECT * FROM ebay_categories
                 WHERE parent = '$_[0]' AND ID != parent});

              Für mich sieht es so aus, als ob di hier alle Datensätzemit der parent ID $_[0] abfragst.
              Ganz genau! außer dem der selbst parent ist...

              und dazu brauchst du eine Rekursive Funktion?

              Ist das nicht das Gleiche wie:

              my @vars = sql(qq{
              SELECT * FROM ebay_categories
              WHERE ID != parent SORT BY parent});

              Nein
              SELECT * FROM ebay_categories WHERE ID != parent SORT BY parent
              Nein diese abfrage gibt alle einträge außer die parents zurück
              also ca. 86000

              Diese
              SELECT * FROM ebay_categories WHERE parent = '$_[0]' AND ID != parent});
              gibt nur die entsprechenden childs von $_[0] zurück...
              das sind so zwischen 0 und 30 Datensätze

              my $count = (@vars/4);

              Also hier die Anzahl der Datnesätze durch 4.
              Das bringt die &sql funktion so mit, die liefert ein array der ergebnismenge, also pro datensatz 4 einträge im array, /4 = anzahl der datensätze

              Benutzt Du nicht das DBI Modul?

              Doch klar, in der sql subroutine

              Struppi.

              Markus

              1. Ist das nicht das Gleiche wie:

                my @vars = sql(qq{
                SELECT * FROM ebay_categories
                WHERE ID != parent SORT BY parent});
                Nein
                SELECT * FROM ebay_categories WHERE ID != parent SORT BY parent
                Nein diese abfrage gibt alle einträge außer die parents zurück
                also ca. 86000

                Diese
                SELECT * FROM ebay_categories WHERE parent = '$_[0]' AND ID != parent});
                gibt nur die entsprechenden childs von $_[0] zurück...
                das sind so zwischen 0 und 30 Datensätze

                und wozu die Rekursion?

                my $count = (@vars/4);

                Also hier die Anzahl der Datnesätze durch 4.
                Das bringt die &sql funktion so mit, die liefert ein array der ergebnismenge, also pro datensatz 4 einträge im array, /4 = anzahl der datensätze

                Benutzt Du nicht das DBI Modul?
                Doch klar, in der sql subroutine

                Aber was ist das für eine seltsame Struktur?
                Du liest die komepltten Daten ein und erzeugst dann Array in dem jeweils 4 Einträge einem Datensatz entsprechen?

                Warum nicht

                $vars = [
                [satz_1_feld_1, satz_1_feld_2, satz_1_feld_3, satz_1_feld_4],
                [satz_2_feld_1, satz_2_feld_2, satz_2_feld_3, satz_2_feld_4]
                ];

                also eine Struktur die auch den Daten entspricht? Oder die von der DBI Abfrage zurück kommt (ich nehme an du nimmst fetchrow_arrayref)

                Struppi.

                1. und wozu die Rekursion?

                  um die Baumstruktur zu erzeugen

                  nochmal:
                    ich hole Datum 1 aus der Datenbank,
                    dann rufe ich die Funktion erneut auf um  --+
                    alle Kinder von Datum1 zu ermitteln         } Rekursion bis +
                    dann rufe ich die Funktion erneut auf um  --+               |
                    alle Kinder von Kindern zu ermitteln                        |
                                                                                |
                  +-------------------------------------------------------------+
                  |
                  +---> @vars true ist, also solange ein ergebnis geliefert wird

                  Das ganze möchte ich dann als Baum anzeigen... etwa wie im Windows explorer die Dateileiste
                  Das ganze funktionier wie gesagt auch, jedenfalls mit meinen testdateien

                  Aber was ist das für eine seltsame Struktur?
                  Du liest die komepltten Daten ein und erzeugst dann Array in dem jeweils 4 Einträge einem Datensatz entsprechen?

                  Warum nicht

                  $vars = [
                  [satz_1_feld_1, satz_1_feld_2, satz_1_feld_3, satz_1_feld_4],
                  [satz_2_feld_1, satz_2_feld_2, satz_2_feld_3, satz_2_feld_4]
                  ];

                  also eine Struktur die auch den Daten entspricht? Oder die von der DBI Abfrage zurück kommt (ich nehme an du nimmst fetchrow_arrayref)

                  Das ist historisch gewachsen :)

                  Struppi.

              2. Tag Markus.

                Benutzt Du nicht das DBI Modul?
                Doch klar, in der sql subroutine

                Warum lässt du deine Subroutine nicht einfach 0 zurückliefern, wenn es keine Ergebnisse gibt, dann sollte sich der rekursive Aufruf der Funktion auch besser steuern lassen. Und eventuell solltest du mit Referenzen arbeiten, das könnte effizienter sein:

                sub abfrage {  
                  my $statement = shift;  
                  my $sth = $dbh->prepare($statement);  
                  $sth->execute();  
                  my $rows = $sth->rows;  
                  if ($rows <= 0) {  
                    return 0;  
                  }  
                  else {  
                    my @rows = $sth->fetchrow_array();  
                    return \@rows;  
                  }  
                }  
                  
                my $ret = abfrage(...);  
                if($ret) {  
                  # tu was  
                  # neuer Aufruf von abfrage  
                }
                

                Ungetestet runtergetippt, Fehler bitte ich nachzusehen.

                Siechfred

                1. 'Ungetestet runtergetippt' entspricht so ziemlich meiner subroutine sql,

                  nur dass diese nicht 0 zurückgibt...
                  werd ich mal einbauen

                  thx, markus

      3. Moin!

        Die Datenbanktstruktur sieht wie folgt aus:
        +-------+--------+-------+--------+
        | ID    | name   | layer | parent |
        +-------+--------+-------+--------+
        |   1   | Home   |     0 |      1 |
        |   2   | test1  |     1 |      1 |
            .       .        .        .
            .       .        .        .
            .       .        .        .
        | 38086 | test2  |     1 |      1 |
        | 38087 | blabla |     2 |      1 |
        +-------+--------+-------+--------+

        my @vars = sql(qq{
           SELECT * FROM ebay_categories
           WHERE parent = '$_[0]' AND ID != parent});
          my $count = (@vars/4);

        SELECT * ist ja grundsätzlich böse - in diesem Fall aber nochmal extraböse!

        Denn du verläßt dich drauf, dass dein * im SELECT immer genau 4 Spalten auswirft. Änderst du irgendwann in der Datenbank die Struktur, dann hast du hier in deinem Code ganz böse viele Anpassungsprobleme, weil du nicht mehr durch 4 teilen mußt, sondern durch die neue Anzahl von Spalten, die in der DB stecken.

        Generell finde ich das gewählte Datenformat für die Speicherung der Datensätze sehr unglücklich. Ein zweidimensionales Array wäre wesentlich angemessener gewesen, eventuell auch eine Kombination aus Array und Hash (um auf die Spalten der Datenbank zuzugreifen - der Code wird wesentlich verständlicher).

        Das Problem ist auch, dass die Zahl 4 nicht als veränderbare Konstante definiert ist, sondern als Zahl an allen möglichen Stellen vorkommt - eventuell auch dort, wo "4" nicht die Zahl der Spalten aus dem SQL-Statement meint. Spätere Änderungen sind also durchaus problematisch.

        Ich gebe zu, dass das alles mit deinem Hauptproblem wenig zu tun hat, aber es ist IMO eine Frage des grundsätzlichen Stils, den man durchaus mal diskutieren sollte. Ab einem gewissen Kenntnislevel kann man sich im Programmieren eben nur noch im Stil verbessern. :)

        - Sven Rautenberg

        --
        My sssignature, my preciousssss!
        1. Moin!

          Tach Sven!

          Die Datenbanktstruktur sieht wie folgt aus:
          +-------+--------+-------+--------+
          | ID    | name   | layer | parent |
          +-------+--------+-------+--------+
          |   1   | Home   |     0 |      1 |
          |   2   | test1  |     1 |      1 |
              .       .        .        .
              .       .        .        .
              .       .        .        .
          | 38086 | test2  |     1 |      1 |
          | 38087 | blabla |     2 |      1 |
          +-------+--------+-------+--------+

          my @vars = sql(qq{
             SELECT * FROM ebay_categories
             WHERE parent = '$_[0]' AND ID != parent});
            my $count = (@vars/4);

          SELECT * ist ja grundsätzlich böse - in diesem Fall aber nochmal extraböse!

          Denn du verläßt dich drauf, dass dein * im SELECT immer genau 4 Spalten auswirft. Änderst du irgendwann in der Datenbank die Struktur, dann hast du hier in deinem Code ganz böse viele Anpassungsprobleme, weil du nicht mehr durch 4 teilen mußt, sondern durch die neue Anzahl von Spalten, die in der DB stecken.

          Generell finde ich das gewählte Datenformat für die Speicherung der Datensätze sehr unglücklich. Ein zweidimensionales Array wäre wesentlich angemessener gewesen, eventuell auch eine Kombination aus Array und Hash (um auf die Spalten der Datenbank zuzugreifen - der Code wird wesentlich verständlicher).

          Ja, dazu muß ich Dir auf jeden Fall recht geben!
          ich habe mal (irgendwann früher :-))diese unterfunktion sql geschrieben, um mir den Zugriff auf die Datenbank zu vereinfachen.
          so kann ich halt einfach scalar = sql($datenbankabfrage) aufrufen und die gewünschten Daten sind im array... man ist ja schreibfaul.

          Sicher ist es nicht grade glücklich, das geb ich gern zu :) und ich werde das auch bestimmt demnächst mal ändern, weil's mir teils selbst auf den Sack geht... aber hier ist nochmals zu erwähnen :man ist ja faul

          Das Problem ist auch, dass die Zahl 4 nicht als veränderbare Konstante definiert ist, sondern als Zahl an allen möglichen Stellen vorkommt - eventuell auch dort, wo "4" nicht die Zahl der Spalten aus dem SQL-Statement meint. Spätere Änderungen sind also durchaus problematisch.

          Da stimme ich zu!

          Ich gebe zu, dass das alles mit deinem Hauptproblem wenig zu tun hat, aber es ist IMO eine Frage des grundsätzlichen Stils, den man durchaus mal diskutieren sollte. Ab einem gewissen Kenntnislevel kann man sich im Programmieren eben nur noch im Stil verbessern. :)

          Das ist nicht schlimm, ich hab das script nochmal durchgesehen, einige varialblenzuweisungen in schleifen vorgenommen, sodass sie beim rekursionsaufruf nicht überschrieben werden, und schon ging alles :)

          Einziges was ich noch suboptimal halte ist, dass das script 54sek braucht um die 86000+ Datensätze als Baum "aufzumalen"

          Aber irgendwo bekomm ich's nicht optimaler hin... Das mag evtl. daran liegen, dass ich als Dachdeckermeister nicht soviel kenne von sowas hab :)

          • Sven Rautenberg

          Gruß, Markus

          1. Moin!

            Einziges was ich noch suboptimal halte ist, dass das script 54sek braucht um die 86000+ Datensätze als Baum "aufzumalen"

            Das dürfte höchstwahrscheinlich an der Rekursion liegen.

            Jedes rekursiv lösbare Problem läßt sich auch iterativ lösen. Und in allen Fällen des "Baumwesens" würde ich dringend empfehlen, die Datenbank genau EINMAL nach ALLEN in Frage kommenden Datensätzen zu befragen, und den Aufbau der Baumstruktur dann im RAM-Speicher zu sortieren.

            Wobei die Sache dann auch noch dadurch beschleunigt werden kann, dass du die Inhalte der Datenbank nicht nochmal kopierst, sondern mit Zeigern oder Referenzen arbeitest.

            Aber irgendwo bekomm ich's nicht optimaler hin... Das mag evtl. daran liegen, dass ich als Dachdeckermeister nicht soviel kenne von sowas hab :)

            Das ist keine akzeptable Ausrede.

            - Sven Rautenberg

            --
            My sssignature, my preciousssss!
          2. Hi,

            Aber irgendwo bekomm ich's nicht optimaler hin...

            Das kann ja schon aus prinzipiellen Gründen nicht gehen.

            cu,
            Andreas

            --
            Warum nennt sich Andreas hier MudGuard?
            Schreinerei Waechter
            Fachfragen per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.