Perl 'Expert' Quiz-of-the-Week #5

Mark Jason Dominus <[email protected]> Wed, 13 Nov 2002 18:19:05 -0500
Newsgroups gmane.comp.lang.perl.qotw.quiz-of-the-week
Organization Plover Systems
Message-ID <[email protected]>
IMPORTANT: Please do not post solutions, hints, or other spoilers
        until at least 60 hours after the date of this message.
        Thanks.

IMPORTANTE: Por favor, no enviéis soluciones, pistas, o cualquier otra
        cosa que pueda echar a perder la resolución del problema hasta
        que hayan pasado por lo menos 60 horas desde el envío de este
        mensaje. Gracias.

IMPORTANT: S'il vous plaît, attendez au minimum 60 heures après la
        date de ce message avant de poster solutions, indices ou autres
        révélations. Merci.

WICHTIG: Bitte schicken Sie keine Lösungen, Tipps oder Hinweise für
        diese Aufgabe vor Ablauf von 60 Stunden nach dem Datum dieser
        Mail. Danke.

Qing3 Zhu4Yi4: Qing3 Ning2 Deng3Dao4 Jie1Dao4 Ben3 Xin4Xi2 Zhi1Hou4 60
        Xiao3Shi2, Zai4 Fa1Biao3 Jie3Da2, Ti2Shi4, Huo4 Qi2Ta1 Hui4
        Xie4Lou4 Da2An4 De5 Jian4Yi4.  Xie4Xie4.

----------------------------------------------------------------

You will write a function that lays out tree diagrams.

layout_tree's arguments will be (1) an object that represents a tree,
and (2) an optional hash containing some configuration parameters.
The tree object is opaque to your function, but is guaranteed to
support at least the following methods:

        $tree->children

          Returns a list of tree objects that represent the subtrees
          of this tree

        $tree->text

          Returns a reference to an array of strings that show how the
          root node of this tree should be displayed.

An example $tree object might be manufactured by the following class:

        package ExampleTree;

        # Create a new tree object
        # arguments: ->(label of root node,
        #               [arguments for child 1],
        #               [arguments for child 2],
        #               ...);
        sub new {
          my ($pack, $label, @subnodes) = @_;
          my @subobjects = map $pack->new(@$_), @subnodes;
          bless [$label, @subobjects] => $pack;
        }

        # If the label of the tree node is 'foo',
        # we format it like this:
        #    +-----+
        #    | foo |
        #    +-----+
        sub text {
          my $self = shift;
          my $label = $self->[0];
          my $ln = length $label;
          $label .= " " x (5 - $ln) if $ln < 5;  # pad with spaces to width 5
          $ln = length $label;
          my $top = "+" . "-" x $ln . "+";
          return [$top, "|$label|", $top];
        }

        sub children {
          my $self = shift;
          my @children = @$self;
          shift @children;  # throw away the label
          @children;
        }
          
And then one might create such an object by calling

        my $t = ExampleTree->new("5",
                                 ["4 1", 
                                  ["3 1 1", 
                                   ["2 1 1 1",
                                    ["1 1 1 1 1"],
                                   ],
                                  ],
                                  ["2 2 1"]
                                 ], 
                                 ["3 2"]);


layout_tree($t) would then manufacture an array of strings, which,
when printed, would look something like this:

                   +-----+
                   |5    |
                   +-----+
                   ,-' `--------.
               +-----+        +-----+
               |4 1  |        |3 2  |
               +-----+        +-----+
              ,-'  `----.
          +-----+    +-----+
          |3 1 1|    |2 2 1|
          +-----+    +-----+
             |
         +-------+
         |2 1 1 1|
         +-------+
             |
        +---------+
        |1 1 1 1 1|
        +---------+

Details of exactly how this is laid out and what it looks like are up
to you; you might produce output like this instead:


        +-----+   +-----+   +-----+   +-------+   +---------+
        |5    |-+-|4 1  |-+-|3 1 1|---|2 1 1 1|---|1 1 1 1 1|
        +-----+ | +-----+ | +-----+   +-------+   +---------+
                |         |
                |         | +-----+
                |         +-|2 2 1|
                |           +-----+
                | +-----+
                +-|3 2  |
                  +-----+

However, the tree node displays returned by ->text must appear in the
output exactly as they were returned, and it must be possible to
understand the tree structure from looking at the output.