home *** CD-ROM | disk | FTP | other *** search
/ Netrunner 2004 October / NETRUNNER0410.ISO / regular / ActivePerl-5.8.4.810-MSWin32-x86.msi / _d4fb1f75329731ecb9f53d4b740dc270 < prev    next >
Text File  |  2004-06-01  |  10KB  |  330 lines

  1.  
  2. # This is a .pm just to (try to) make some CPAN document converters
  3. #  convert it happily as part of the dist's documentation tree.
  4. package HTML::Element::traverse;
  5.  # Time-stamp: "2002-11-22 23:53:39 MST"
  6. use HTML::Element ();
  7. $VERSION = $VERSION = $HTML::Element::VERSION;
  8. 1;
  9.  
  10. __END__
  11.  
  12. =head1 NAME
  13.  
  14. HTML::Element::traverse - discussion of HTML::Element's traverse method
  15.  
  16. =head1 SYNOPSIS
  17.  
  18.   # $element->traverse is unnecessary and obscure.
  19.   #   Don't use it in new code.
  20.  
  21. =head1 DESCRIPTION
  22.  
  23. C<HTML::Element> provides a method C<traverse> that traverses the tree
  24. and calls user-specified callbacks for each node, in pre- or
  25. post-order.  However, use of the method is quite superfluous: if you
  26. want to recursively visit every node in the tree, it's almost always
  27. simpler to write a subroutine does just that, than it is to bundle up
  28. the pre- and/or post-order code in callbacks for the C<traverse>
  29. method.
  30.  
  31. =head1 EXAMPLES
  32.  
  33. Suppose you want to traverse at/under a node $tree and give elements
  34. an 'id' attribute unless they already have one.
  35.  
  36. You can use the C<traverse> method:
  37.  
  38.   {
  39.     my $counter = 'x0000';
  40.     $start_node->traverse(
  41.       [ # Callbacks;
  42.         # pre-order callback:
  43.         sub {
  44.           my $x = $_[0];
  45.           $x->attr('id', $counter++) unless defined $x->attr('id');
  46.           return HTML::Element::OK; # keep traversing
  47.         },
  48.         # post-order callback:
  49.         undef
  50.       ],
  51.       1, # don't call the callbacks for text nodes
  52.     );
  53.   }
  54.  
  55. or you can just be simple and clear (and not have to understand the
  56. calling format for C<traverse>) by writing a sub that traverses the
  57. tree by just calling itself:
  58.  
  59.   {
  60.     my $counter = 'x0000';
  61.     sub give_id {
  62.       my $x = $_[0];
  63.       $x->attr('id', $counter++) unless defined $x->attr('id');
  64.       foreach my $c ($x->content_list) {
  65.         give_id($c) if ref $c; # ignore text nodes
  66.       }
  67.     };
  68.     give_id($start_node);
  69.   }
  70.  
  71. See, isn't that nice and clear?
  72.  
  73. But, if you really need to know:
  74.  
  75. =head1 THE TRAVERSE METHOD
  76.  
  77. The C<traverse()> method is a general object-method for traversing a
  78. tree or subtree and calling user-specified callbacks.  It accepts the
  79. following syntaxes:
  80.  
  81. =over
  82.  
  83. =item $h->traverse(\&callback)
  84.  
  85. =item or $h->traverse(\&callback, $ignore_text)
  86.  
  87. =item or $h->traverse( [\&pre_callback,\&post_callback] , $ignore_text)
  88.  
  89. =back
  90.  
  91. These all mean to traverse the element and all of its children.  That
  92. is, this method starts at node $h, "pre-order visits" $h, traverses its
  93. children, and then will "post-order visit" $h.  "Visiting" means that
  94. the callback routine is called, with these arguments:
  95.  
  96.     $_[0] : the node (element or text segment),
  97.     $_[1] : a startflag, and
  98.     $_[2] : the depth
  99.  
  100. If the $ignore_text parameter is given and true, then the pre-order
  101. call I<will not> be happen for text content.
  102.  
  103. The startflag is 1 when we enter a node (i.e., in pre-order calls) and
  104. 0 when we leave the node (in post-order calls).
  105.  
  106. Note, however, that post-order calls don't happen for nodes that are
  107. text segments or are elements that are prototypically empty (like "br",
  108. "hr", etc.).
  109.  
  110. If we visit text nodes (i.e., unless $ignore_text is given and true),
  111. then when text nodes are visited, we will also pass two extra
  112. arguments to the callback:
  113.  
  114.     $_[3] : the element that's the parent
  115.              of this text node
  116.     $_[4] : the index of this text node
  117.              in its parent's content list
  118.  
  119. Note that you can specify that the pre-order routine can
  120. be a different routine from the post-order one:
  121.  
  122.     $h->traverse( [\&pre_callback,\&post_callback], ...);
  123.  
  124. You can also specify that no post-order calls are to be made,
  125. by providing a false value as the post-order routine:
  126.  
  127.     $h->traverse([ \&pre_callback,0 ], ...);
  128.  
  129. And similarly for suppressing pre-order callbacks:
  130.  
  131.     $h->traverse([ 0,\&post_callback ], ...);
  132.  
  133. Note that these two syntaxes specify the same operation:
  134.  
  135.     $h->traverse([\&foo,\&foo], ...);
  136.     $h->traverse( \&foo       , ...);
  137.  
  138. The return values from calls to your pre- or post-order
  139. routines are significant, and are used to control recursion
  140. into the tree.
  141.  
  142. These are the values you can return, listed in descending order
  143. of my estimation of their usefulness:
  144.  
  145. =over
  146.  
  147. =item HTML::Element::OK, 1, or any other true value
  148.  
  149. ...to keep on traversing.
  150.  
  151. Note that C<HTML::Element::OK> et
  152. al are constants.  So if you're running under C<use strict>
  153. (as I hope you are), and you say:
  154. C<return HTML::Element::PRUEN>
  155. the compiler will flag this as an error (an unallowable
  156. bareword, specifically), whereas if you spell PRUNE correctly,
  157. the compiler will not complain.
  158.  
  159. =item undef, 0, '0', '', or HTML::Element::PRUNE
  160.  
  161. ...to block traversing under the current element's content.
  162. (This is ignored if received from a post-order callback,
  163. since by then the recursion has already happened.)
  164. If this is returned by a pre-order callback, no
  165. post-order callback for the current node will happen.
  166. (Recall that if your callback exits with just C<return;>,
  167. it is returning undef -- at least in scalar context, and
  168. C<traverse> always calls your callbacks in scalar context.)
  169.  
  170. =item HTML::Element::ABORT
  171.  
  172. ...to abort the whole traversal immediately.
  173. This is often useful when you're looking for just the first
  174. node in the tree that meets some criterion of yours.
  175.  
  176. =item HTML::Element::PRUNE_UP
  177.  
  178. ...to abort continued traversal into this node and its parent
  179. node.  No post-order callback for the current or parent
  180. node will happen.
  181.  
  182. =item HTML::Element::PRUNE_SOFTLY
  183.  
  184. Like PRUNE, except that the post-order call for the current
  185. node is not blocked.
  186.  
  187. =back
  188.  
  189. Almost every task to do with extracting information from a tree can be
  190. expressed in terms of traverse operations (usually in only one pass,
  191. and usually paying attention to only pre-order, or to only
  192. post-order), or operations based on traversing. (In fact, many of the
  193. other methods in this class are basically calls to traverse() with
  194. particular arguments.)
  195.  
  196. The source code for HTML::Element and HTML::TreeBuilder contain
  197. several examples of the use of the "traverse" method to gather
  198. information about the content of trees and subtrees.
  199.  
  200. (Note: you should not change the structure of a tree I<while> you are
  201. traversing it.)
  202.  
  203. [End of documentation for the C<traverse()> method]
  204.  
  205. =head2 Traversing with Recursive Anonymous Routines
  206.  
  207. Now, if you've been reading
  208. I<Structure and Interpretation of Computer Programs> too much, maybe
  209. you even want a recursive lambda.  Go ahead:
  210.  
  211.   {
  212.     my $counter = 'x0000';
  213.     my $give_id;
  214.     $give_id = sub {
  215.       my $x = $_[0];
  216.       $x->attr('id', $counter++) unless defined $x->attr('id');
  217.       foreach my $c ($x->content_list) {
  218.         $give_id->($c) if ref $c; # ignore text nodes
  219.       }
  220.     };
  221.     $give_id->($start_node);
  222.     undef $give_id;
  223.   }
  224.  
  225. It's a bit nutty, and it's I<still> more concise than a call to the
  226. C<traverse> method!
  227.  
  228. It is left as an exercise to the reader to figure out how to do the
  229. same thing without using a C<$give_id> symbol at all.
  230.  
  231. It is also left as an exercise to the reader to figure out why I
  232. undefine C<$give_id>, above; and why I could achieved the same effect
  233. with any of:
  234.  
  235.     $give_id = 'I like pie!';
  236.    # or...
  237.     $give_id = [];
  238.    # or even;
  239.     $give_id = sub { print "Mmmm pie!\n" };
  240.  
  241. But not:
  242.  
  243.     $give_id = sub { print "I'm $give_id and I like pie!\n" };
  244.    # nor...
  245.     $give_id = \$give_id;
  246.    # nor...
  247.     $give_id = { 'pie' => \$give_id, 'mode' => 'a la' };
  248.  
  249. =head2 Doing Recursive Things Iteratively
  250.  
  251. Note that you may at times see an iterative implementation of
  252. pre-order traversal, like so:
  253.  
  254.    {
  255.      my @to_do = ($tree); # start-node
  256.      while(@to_do) {
  257.        my $this = shift @to_do;
  258.  
  259.        # "Visit" the node:
  260.        $this->attr('id', $counter++)
  261.         unless defined $this->attr('id');
  262.  
  263.        unshift @to_do, grep ref $_, $this->content_list;
  264.         # Put children on the stack -- they'll be visited next
  265.      }
  266.    }
  267.  
  268. This can I<under certain circumstances> be more efficient than just a
  269. normal recursive routine, but at the cost of being rather obscure.  It
  270. gains efficiency by avoiding the overhead of function-calling, but
  271. since there are several method dispatches however you do it (to
  272. C<attr> and C<content_list>), the overhead for a simple function call
  273. is insignificant.
  274.  
  275. =head2 Pruning and Whatnot
  276.  
  277. The C<traverse> method does have the fairly neat features of
  278. the C<ABORT>, C<PRUNE_UP> and C<PRUNE_SOFTLY> signals.  None of these
  279. can be implemented I<totally> straightforwardly with recursive
  280. routines, but it is quite possible.  C<ABORT>-like behavior can be
  281. implemented either with using non-local returning with C<eval>/C<die>:
  282.  
  283.   my $died_on; # if you need to know where...
  284.   sub thing {
  285.     ... visits $_[0]...
  286.     ... maybe set $died_on to $_[0] and die "ABORT_TRAV" ...
  287.     ... else call thing($child) for each child...
  288.     ...any post-order visiting $_[0]...
  289.   }
  290.   eval { thing($node) };
  291.   if($@) {
  292.     if($@ =~ m<^ABORT_TRAV>) {
  293.       ...it died (aborted) on $died_on...
  294.     } else {
  295.       die $@; # some REAL error happened
  296.     }
  297.   }
  298.  
  299. or you can just do it with flags:
  300.  
  301.   my($abort_flag, $died_on);
  302.   sub thing {
  303.     ... visits $_[0]...
  304.     ... maybe set $abort_flag = 1; $died_on = $_[0]; return;
  305.     foreach my $c ($_[0]->content_list) {
  306.       thing($c);
  307.       return if $abort_flag;
  308.     }
  309.     ...any post-order visiting $_[0]...
  310.     return;
  311.   }
  312.  
  313.   $abort_flag = $died_on = undef;
  314.   thing($node);
  315.   ...if defined $abort_flag, it died on $died_on
  316.  
  317. =head1 SEE ALSO
  318.  
  319. L<HTML::Element>
  320.  
  321. =head1 COPYRIGHT
  322.  
  323. Copyright 2000,2001 Sean M. Burke
  324.  
  325. =head1 AUTHOR
  326.  
  327. Sean M. Burke, E<lt>sburke@cpan.orgE<gt>
  328.  
  329. =cut
  330.