Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi, PMs. I am working on a minimally functional discussion forum that reads record/messages from a flatfile ASCII database. All the messages have relationships to other messages (i.e. parent and children messages), and I am trying to create a "tree" view for a list of messages. I sort of succeeded, but the messages are in the reverse order. I need help (in more ways than one at this point).

Each record has a record ID, a parent field, a space-delimited list of children for a field, and some message data fields. A function called &get_record takes a record id, and puts the record in a hash. I usually display records with a foreach loop.

The HTML display function for the tree view looks like:

# @ids are a list of record IDs to display. they're sorted by id beca +use newer messages have higher id numbers @sorted_ids = reverse sort { $a cmp $b } @ids; foreach (@sorted_ids) { # this function displays a tree for each id given. the function wil f +ollow. &do_tree ($_); } sub do_tree { # -------------------------------------------------------- # this is the tree loop that call itself to display records in # tree form. pass it some record ids and see what happens! my %rec = &get_record ($_[0]); my @children; my @kids = split (/ /, $rec{'Children'}); # get all the kids foreach $kid (@kids) { push (@children, $kid) if ($kid); } # remove +blanks if (@children) { # if kids then loop @sorted_children = sort { $a cmp $b } @children; print "<li>[$rec{'ID'}] $rec{'Subject'}<ul>\n"; foreach $child (@sorted_children) { # here, the function calls itself. is there a better way to do this o +r am i doing this wrong? &do_tree ($child); } print "</ul></li>\n"; } else { # no kids? actually display the record then print "<li>[$rec{'ID'}] $rec{'Subject'}</li>\n"; } }

When I take out the sorting code, things work. When the sorting code is in, the messages don't all display. I double-checked the database and the rest of my code, and I'm pretty sure the problem lies with how I wrote do_tree. I'm pretty stuck on this, and I've misplaced my Perl Cookbook. Any help would be greatly appreciated.

Currently, the forum can be found here.

- Leam

Replies are listed 'Best First'.
Re: Recursion question...
by japhy (Canon) on Dec 14, 2001 at 23:29 UTC
    You need to my() the @sorted_children array! Otherwise, you overwrite it!

    Better yet, don't use the array, and use the default sort().

    if (@children) { print "<li>[$rec{'ID'}] $rec{'Subject'}<ul>\n"; for my $child (sort @children) { do_tree($child); } print "</ul></li>\n"; }
    Notice how I left out the {$a cmp $b} part -- that's how sorting behaves by default.

    _____________________________________________________
    Jeff[japhy]Pinyan: Perl, regex, and perl hacker.
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

      Anonymous Monk, that's a nice code you wrote ;-).
      
      As I looked at it, though, I couldn't help but 
      hack it a bit here and there.  My hope was to shorten
      it up a bit (which, who knows, may lead to some
      performance improvements? ;).  So, if you like the
      change just stick to it... I'd be glad to know that
      it was useful to you. 
      
      Anyhow, here we go:
      
      sub do_tree { # -------------------------------------------------------- # this is the tree loop that call itself to display records in # tree form. pass it some record ids and see what happens! my %rec = &get_record ($_[0]); # get all the kids; weed out empty ones my @children = sort grep( {length} , split (/ /, $rec{'Children'})); + # no kids? actually display the record then print "<li>[$rec{'ID'}] $rec{'Subject'}</li>\n" unless ($#children); # kinds actually exist? display them... print "<li>[$rec{'ID'}] $rec{'Subject'}<ul>\n"; do_tree($child) for my $child (@children); print "</ul></li>\n"; }
      ps. i didn't test it or anything... however, i feel like it should work without much hassle on your part.


      --
      print join(" ", map { sprintf "%#02x", $_ }unpack("C*",pack("L",0x1234 +5678)))
        Specifically, you didn't test this line: do_tree($child) for my $child (@children); A post-fix for must operate on $_ -- you're not allowed to give an iterator variable. do_tree($_) for @children;

        _____________________________________________________
        Jeff[japhy]Pinyan: Perl, regex, and perl hacker.
        s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;