NAME AI::Menu SYNOPSIS use AI::Menu; my $factory = new AI::Menu::Factory; my $menu = $factory->generate($hash_of_functions); my $menu = $factory->generate($hash_of_functions, $hash_of_categories); my $menu = $factory->generate($graph); DESCRIPTION An "AI::Menu::Factory" object generates "Tree::Nary" objects from directed graphs (Graph::Directed or an object with the same methods) or a description of the function set. The algorithm is not very efficient (approximately O(F^6), F being the number of functions). It is also not quite as intelligent as it should be. You should cache the results instead of repeatedly calculating them. As the algorithm is optimized or more efficient algorithms are found, they will be incorporated. The interface for generating the trees should not change too much. The resulting object might become a Tree::Nary object encased in an AI::Menu object. METHODS All of the following methods (except generate) are available in the "new" function when creating the "AI::Menu::Factory" object. generate This function does some housekeeping before calling a configurable module to generate the tree. If called with a single hash reference, the hash is assumed to be a list of functions mapping to array references containing a list of categories. It is further assumed that the sets of function names and category names are disjoint. A closure is created for the "leaf_q" function which returns true if its argument is a key in the hash reference. The complete graph is created from this single hash reference: if a category can reach another category through a function, then an edge is inserted between the two categories. This edge is bidirectional. If called with two hash references, the first hash is treated as before, but the second hash reference is considered a mapping of categories to categories. This second hash is used instead of automatically generating the information from the first hash. If called with a single object that is not a hash reference, then the argument is considered a graph object (usually of Graph::Directed). The "leaf_q" function will need to be defined. leaf_q This function returns true if the argument represents a function (leaf in the graph). It returns false if the argument represents a category. This may be set either when the "AI::Menu::Factory" object is created or through a method call. The method call with no argument returns the current function. maker This is the package used to create the menu from the graph. The following call is made: my $menu = $self -> {maker} -> new( width => $self->{width}, weight_f => $self -> {weight_f}, leaf_q => $leafq, ); return $menu -> generate_tree($g, $optscore); The "$optscore" value is the score for the optimum tree. Once a tree is found with this score, searching should stop. new Creates an "AI::Menu::Factory" object. Optional arguments are key/value pairs taken from this list of methods except for "generate" and "new". weight_f This function is used to calculate the edge weights in the graph. It is called with four arguments: the object generating the tree, the graph object, the originating vertex, the destination vertex. The function should return "undef" for an infinite weight. width This is the desired number of children per node. The optimal number (and default) is three. EXAMPLE The following example illustrates generating a menu from a list of functions and printing the resulting tree using LaTeX. my $factory = AI::Menu::Factory; my $functions = { a => [qw: A B D :], b => [qw: C D E :], c => [qw: A B C :], d => [qw: E G H :], e => [qw: A C E :], f => [qw: A D F :], }; $menu = $factory -> generate($functions); # following borrowed from Tree::Nary's test.pl sub node_build_string() { my ($node, $ref_of_arg) = (shift, shift); my $p = $ref_of_arg; my $string; my $c = $node->{data}; if(defined($p)) { $string = $$p; } else { $string = ""; } if($node -> is_leaf($node)) { $c = "\\leaf{\\mbox{ $c }}\n"; } else { $c = "\\branch{" . $node -> n_children( $node ) . "}{$c}\n"; } $string .= $c; $$p = $string; return($Tree::Nary::FALSE); } my $string; $menu -> traverse( $menu, $Tree::Nary::POST_ORDER, $Tree::Nary::TRAVERSE_ALL, -1, \&node_build_string, \$string); print "$string\n"; The above code prints out the following: \leaf{\mbox{ a }} \leaf{\mbox{ c }} \branch{2}{B} \leaf{\mbox{ b }} \leaf{\mbox{ d }} \leaf{\mbox{ e }} \branch{3}{C} \leaf{\mbox{ f }} \branch{3}{A} This corresponds to the following tree: A / | \ f C B /|\ |\ b d e c a BUGS The optimal score is fixed at one. This means all possibilities are searched each time. We need an algorithm that maps number of functions to optimal score for a tree with arbitrary $width parameter. While the algorithm seems inefficient, I am not aware of how far it is from most efficient. Some work remains in this area. The tree can be a bit strange. In fact, it usually is. The results should be considered academic or just hints at this point. If you have an interesting set of functions and categories, send them to me. The algorithm needs some way to make certain categories more important than others. SEE ALSO the Graph::Directed manpage, the Tree::Nary manpage. AUTHOR James Smith COPYRIGHT Copyright (C) 2001 Texas A&M University. All Rights Reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.