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

I am attempting to write a function in which a clearcase directory version, of the form /vobs/engine_cdma2000/code@@/main/<some_branch>/<version_number> has @@ .etc stripped away, and then has the other information stored in a tree of the form:

"/vobs" <-- root
|
V
"/vobs/engine_cdma2000"
|
V
"/vobs/engine_cdma2000/code"


Each of the above Tree::Nary nodes holds as its data a pointer to a hash. Hash contents for the lowest of the nodes shown above would be:

$hash_data{dir_name} = "/vobs/engine_cdma2000/code"
$hash_data{dir_vep} = "/vobs/engine_cdma2000/code@@/main/<some_branch>/<version_number>"
$hash_data{needs_merge} = TRUE;

The following is the content of my input file, integration_branch_directories.txt:
/vobs/engine_cdma2000/code@@/main/par_x_haloc9_s74.47.9/bld_01.06.00_ca25_krmt43_haloc/1
/vobs/engine_cdma2000/code/dmss@@/main/par_x_haloc9_s74.47.9/bld_01.06.00_ca25_krmt43_haloc/1

The following is my program. Its big hangup is that Tree::Nary->find is not working, it is reporting as "already in tree" node data contents that are not in any existing tree node. Could it be that Tree::Nary->find doesn't like a Tree::Nary node's data field to be a pointer to a hash? Here is my code. I would appreciate any help you could give.

#!/vobs/atso_tools/perl5/bin/perl -w # LATEST VERSION # This home UNIX account directory contains modules # downloaded from www.cpan.org use lib "/home/ark014/bin/trees"; # For N-ary trees use Tree::Nary; # For splitdir use File::Spec; use strict; use English; use strict; use Env; use integer; use File::Basename; use File::Path; use File::Copy; use File::Spec; my $parent_node; use constant TRUE => 1; use constant FALSE => 0; my @dir_array; my @dir_sort; my @dir_name; # Roots of an N-ary tree of directories my $root_advanced; # Node of an N-ary tree my $node; my $dir_list; my $dir_list_sorted; my $dir_name_list; my $label; my $pwd; ###################################################################### +######### # NAME: init_dirs # # DESCRIPTION: 1) Define test label and test directory. # 2) Define variables associated with the input file, # integration_branch_directories.txt, and a file # of sorted directories (latter is intended to speed c +onstruction # of N-ary tree). ###################################################################### +######### sub init_dirs { $label = "HALOC9_X_01.06.00R_NARY"; $pwd = $ENV{'HOME'}."/builds/".$label; $dir_list = "$pwd/integration_branch_directories.txt"; $dir_list_sorted = "$pwd/integration_branch_directories_sorted.txt" +; } ###################################################################### +######### # NAME: create_dir_arrays # # DESCRIPTION: 1) Open pointers to input file and sorted dirs file # 2) Create arrays of directory versions, and of sorted # directory versions # 3) Also create "dir_names", an array (sorted) of direct +ories # which have had "@@/main/<dir_path>/bld_branch/ver_nu +m" # stripped away ###################################################################### +######### sub create_dir_arrays { my $dir_vep; my $dir_name; my $dir_name2; my $debug1 = TRUE; my $debug2 = TRUE; my $dir; open DIRLIST, "$dir_list" or die "Cannot open $dir_list"; chomp (@dir_array = (<DIRLIST>)); close DIRLIST; open DIRLIST, "$dir_list" or die "Cannot open $dir_list"; chomp (@dir_sort = (<DIRLIST>)); close DIRLIST; open DIRSORT, ">>$dir_list_sorted" or die "Cannot open $dir_list_so +rted"; @dir_sort = sort{$b cmp $a}(@dir_sort); close DIRSORT; if($debug2){ print "Contents of dir_array\n"; foreach $dir (@dir_array){ print "$dir"; print " "; } } if($debug2){ print "\nContents of dir_sort\n"; foreach $dir (@dir_sort){ print "$dir"; print " "; } } # To construct a tree of dirs need to get rid of # "@@/main/<dir_path>/bld_branch/ver_num" # This @dir_name array will hold them foreach $dir_vep (@dir_sort) { $dir_name = $dir_vep; $dir_name =~ s/^(.*)@@.*/$1/; push(@dir_name, $dir_name); } if($debug1){ print "\nContents of dir_name\n"; foreach $dir (@dir_name){ print "$dir"; print " "; } print "\n"; } } ###################################################################### +######### # NAME: dir_tree_searches # # DESCRIPTION: 1) For debugging only # 2) Allows an N-ary tree to be searched to see if it con +tains # a node containing a specific set of data ###################################################################### +######### sub dir_tree_searches { my $ref_to_node_data = shift; my $root = shift; my $debug1 = TRUE; print "Entering dir_tree_searches\n"; my $result; # $root is the root of the dir tree $result = Tree::Nary->find($root, $Tree::Nary::PRE_ORDER, $Tree::Na +ry::TRAVERSE_ALL, $ref_to_node_data); if(defined $result) { print "Have found node holding ref to the input data\n"; my $node_data = $result->{data}; my %hash_data = %$node_data; print "IN DIR_TREE_SEARCHES: result dir_name holds $hash_data{di +r_name}\n"; print "IN DIR_TREE_SEARCHES: result dir_vep holds $hash_data{dir +_vep}\n"; print "IN DIR_TREE_SEARCHES: result needs_merge holds $hash_data +{needs_merge}\n"; } else { print "ERROR, could not found node in dir tree holding ref to th +e input data\n"; } } sub print_node_contents { my $ref_to_node = shift; my $node_data = $ref_to_node->{data}; my %hash_data = %$node_data; print "\nIN PRINT_NODE: node's hex address was $ref_to_node\n"; print "IN PRINT_NODE: node's dir_name holds $hash_data{dir_name}\n" +; print "IN PRINT_NODE: node's dir_vep holds $hash_data{dir_vep}\n"; print "IN PRINT_NODE: node's needs_merge holds $hash_data{needs_mer +ge}\n\n"; } ###################################################################### +######### # NAME: create_nary_dir_tree # # DESCRIPTION: 1) Create an N-ary Tree using Tree::Nary # 2) Input array @dir_name, and helper array @dir_sort, h +ave # had top down text sorts. Sort is intended to reduce + the # amount of "if node present" == TRUE results, and spe +ed # creation of tree. Tree is in no way reliant on top d +own # sort of these arrays, this function would also work +if # arrays were unsorted. # 3) Data field of each node is a pointer to a hash conta +ining # the node's data. ###################################################################### +######### sub create_nary_dir_tree { # Local variables my $dir_name; my $dir; # For looping through directories array my $dir_name_counter = 0; my $i; my $j; # For looping through the dirs in each directory version my $debug1 = TRUE; my $debug2 = FALSE; my $result; # First create data for the root of advanced dir tree my %node_data_adv_root; $node_data_adv_root{"dir_name"} = "/vobs"; $node_data_adv_root{"dir_vep"} = "NULL"; $node_data_adv_root{"needs_merge"} = FALSE; # Create a Tree::Nary tree's root with ref to the above hash %node_ +data_adv_root as its data $root_advanced = Tree::Nary->new(\%node_data_adv_root); print "Printing contents of root\n"; &print_node_contents($root_advanced); # Loop through @dir_name (these versions have had @@/main/<branches +>/bld_branch/ver_num removed foreach $dir_name (@dir_name) { ### @dirname foreach loop - top # Will be used in this loop to help tree construction # $parent_node is the root node, holding "/vobs", at start of th +is loop # Each loop processes a directory version from @dir_name $parent_node = $root_advanced; if($debug1){ # debug print "dir_name is $dir_name\n"; } # debug # To be certain that node_data hash is unique in each loop # Data for the N-ary tree node that will be created my %node_data; # Tokenized version of current @dir_name entry, unique for each +loop. # example /vobs/engine_cdma2000/code becomes an array: # [0] empty string [1] "vobs" [2] "engine_cdma2000" [3] "code" # splitdir array ALWAYS has an empty string at [0] my @splitdir_curr = File::Spec->splitdir($dir_name); # Loop through tokens that represent dir path of the current @di +r_name entry # $splitdir_curr[0] is "" so $j starts at "1" for ($j = 1; $j <= $#splitdir_curr; $j++) { ### @splitdir_curr +foreach loop - top $dir = $splitdir_curr[$j]; if($debug1) { # debug print "\nCREATE_NODE: dir is $dir\n"; print "CREATE_NODE: counter is $j\n\n"; } # debug # Is this the rightmost dir in this entry's dir path? If so: # a) "dir_vep" is not NULL, and is obtained from $dir_sort[$c +ounter] # b) "needs_merge" is TRUE, # c) "dir_name" is obtained from $dir_name # d) This node is guaranteed to be added to the tree if($j == $#splitdir_curr) { # <-- Processing rightmost dir, + its next to "@@" in dirs.txt file print "\nCREATE_NODE: current dir is rightmost dir\n"; $node_data{"dir_vep"} = $dir_sort[$dir_name_counter]; $node_data{"dir_name"} = $dir_name; $node_data{"needs_merge"} = TRUE; } else { # <-- Current dir is not the rightmost dir, start +else # If this is NOT the rightmost dir in this entry's dir pat +h # a) "dir_vep" is "NULL", unless this element was rightmos +t elsewhere in dirs.txt -- may require UPDATE # b) "needs_merge" is FALSE, unless this element was right +most elsewhere in dirs.txt -- may require UPDATE # c) "dir_name" will be built up from splitdirs array # d) This node MAY have been added to tree if its element +was rightmost elsewhere in dirs.txt # e) May need to flip "needs_merge" to TRUE if node was cr +eated earlier and element is rightmost now print "\nCREATE_NODE: current dir is NOT rightmost dir\n"; + $node_data{"dir_vep"} = "NULL"; $node_data{"needs_merge"} = FALSE; # Will be used to construct a dir path my $nonleaf_dir_name = ""; # Building $nonleaf_dir_name from splitdirs array for ($i = 1; $i <= $j; $i++) { # splitdirs loop -- start if(!($nonleaf_dir_name)) { # Avoid null string -- st +art $nonleaf_dir_name = "/" . $splitdir_curr[$i]; } else { # Avoid null string -- else $nonleaf_dir_name = $nonleaf_dir_name . "/" . $sp +litdir_curr[$i]; } # Avoid null string -- end $node_data{dir_name} = $nonleaf_dir_name; if($debug1) { # debug print "\nIN FOR LOOP splitdir_curr[$i] is $splitd +ir_curr[$i]\n"; print "IN FOR LOOP nonleaf_dir_name is $nonleaf_d +ir_name\n\n"; } # debug } # splitdirs loop -- end } # <-- Current dir is not the rightmost dir, end else print "\nCREATE_NODE: Latest node_data is created\n"; print "CREATE_NODE: node_data{dir_name} holds $node_data{d +ir_name}\n"; print "CREATE_NODE: node_data{dir_vep} holds $node_data{di +r_vep}\n"; print "CREATE_NODE: node_data{needs_merge} holds $node_dat +a{needs_merge}\n\n"; # Is there a node in the tree that holds same data as $nod +e_data above? # To get the tree started I created a "/vobs" root node ab +ove, so I don't want to # repeatedly check for that node, for each directory in di +rectories.txt if ( !($node_data{dir_name} eq "/vobs") ) { # if new_node +does NOT contain "/vobs" start if($debug1){ # debug print "Adding a non-root node\n"; } # debug # If node having the data in \%node_data is not in the +tree, add it $result = Tree::Nary->find($root_advanced, $Tree::Nary: +:PRE_ORDER, $Tree::Nary::TRAVERSE_ALL, \%node_data); print "PRIOR to add node, searched for node in tree wit +h same data as current node, result was:\n"; if($result) { &print_node_contents($result); } else { print "find function failed, no node having same dat +a as current node was found\n"; } if($result) { # result = found if($debug1){ # debug print "\nADD_NODE: current node already in tree, +its address is $result\n"; } # debug # This variable will hold the address of the most re +cently found node # as this loop proceeds # Most recently found node becomes "parent" node $parent_node = $result; } else { # else result = NOT found starts print "\nADD_NODE: current node was NOT in tree\n"; # Add the current node to tree, using $parent_node s +aved in previous loop's search # append will make new node the rightmost child of $ +parent_node my $new_node = Tree::Nary->new(\%node_data); print "\nADD_NODE: parent_node address of new_node w +ill be $parent_node\n"; print "ADD_NODE: new_node address is $new_node\n\n"; Tree::Nary->append($parent_node, $new_node); # If node having the data in \%node_data is not in t +he tree, add it $result = Tree::Nary->find($root_advanced, $Tree::Na +ry::PRE_ORDER, $Tree::Nary::TRAVERSE_ALL, \%node_data); print "AFTER add node, searched for node in tree wit +h same data as current node, result was:\n"; if($result) { &print_node_contents($result); } else { print "find function failed, no node having same +data as current node was found\n"; } # Most recently added node, representing a "dir" fro +m a given dir_path # becomes the new "parent" $parent_node = $parent_node->{children}->{next}; } # else result = NOT found ends } else { # else new_node DOES contain "/vobs" print "\nADD_NODE ELSE node_data SHOULD contain /vobs only +, it is the root\n"; print "ADD_NODE ELSE: node_data{dir_name} holds $node_data +{dir_name}\n\n"; } # new_node does NOT contain "/vobs" end } ### @splitdir_curr foreach loop - bottom # $dir_name_counter increment will cause next entry in @dir_name + array to be processed $dir_name_counter++; } ### @dirname foreach loop - bottom } ###################################################################### +######### # NAME: print_nary_dir_tree # # DESCRIPTION: 1) N-ary Tree has all its nodes visited and printed out + # 2) Contents of nodes' data fields are printed out # 3) print_nary_dir_tree has a helper function embedded w +ithin it # called "print_sub", which is used to print out the d +ata field # in each node visited during the N-ary Tree traversal + ###################################################################### +######### sub print_nary_dir_tree { my $tree = shift; my $debug1 = TRUE; print "Entering sub print_nary_dir_tree\n"; my $printsub = sub { my $node = shift; my %hash_data; my $node_data; if (defined $node) { $node_data = $node->{data}; %hash_data = %$node_data; } # If $node is undefined, $node_data will also be undefined if (defined $node_data) { print "IN PRINTSUB: address of current node is $node\n"; print "IN PRINTSUB: hash_data{dir_name} holds $hash_data{di +r_name}\n"; print "IN PRINTSUB: hash_data{dir_vep} holds $hash_data{dir +_vep}\n"; print "IN PRINTSUB: hash_data{needs_merge} holds $hash_data +{needs_merge}\n\n"; } else { print "IN PRINTSUB: printsub if statement has failed, node_ +data undefined\n"; } return 0; }; ### $tree->traverse($tree, $Tree::Nary::PRE_ORDER, $Tree::Nary::TRA +VERSE_ALL, -1, $printsub); Tree::Nary->traverse($tree, $Tree::Nary::PRE_ORDER, $Tree::Nary::TR +AVERSE_ALL, -1, $printsub); } # main program &init_dirs; &create_dir_arrays; &create_nary_dir_tree; &print_nary_dir_tree($root_advanced);

Replies are listed 'Best First'.
Re: Tree::Nary n00b could use some help
by toolic (Bishop) on Sep 09, 2008 at 02:40 UTC
    This is a LOT of code to expect someone to sift through. Do you even know which of your 3 calls to Tree::Nary->find() causes the error? All I can offer is some generic debugging techniques:
    • Re-read some of the advice to use other modules in Tree::Nary n00b has questions.
    • Take some time to reduce your code to less than 20 lines, while still getting the same error. This may take some hand-crafting of small input data.
    • use Data::Dumper; print Dumper(); often
    • Look at the source code for Tree::Nary to figure out what Tree::Nary->find() expects as arguments, if the documentation is not clear.
    • Super Search on "Tree::Nary" to see if anything pops up.