Re: pairing sibling nodes with pre-post-order

[email protected] Wed, 7 Apr 2004 15:49:44 -0700 (PDT)
Newsgroups gmane.lisp.scheme.ssax-sxml
Message-ID <[email protected]>
Hello!

> In using HtmlPrag and SXML tools to extract information from one Web
> page, I need to treat adjacent "td" elements in pairs.
>
> I currently use SXPath to find the "td" elements in a particular part of
> the document, then use "pre-post-order" to transform them into the
> desired SXML.  The following solution uses a variable to save the first
> occurrence in a pair:
>
>     (pre-post-order
>      '((td "A1") (td "A2") (td "B1") (td "B2") (td "C1") (td "C2"))
>      `((td *preorder* .
>            ,(let ((left #f))
>               (lambda (tag . elems)
>                 (if left
>                     (begin0 (append (list '*pair) left elems)
>                       (set! left #f))
>                     (begin (set! left elems)
>                            '())))))))
>     ==> 
>     (() (*pair "A1" "A2") () (*pair "B1" "B2") () (*pair "C1" "C2"))

I'd like to remark first that pre-post-order -- however convenient and
general -- is not the best solution to every possible problem. I guess
my motivation is first to write the code that solves the problem at
hand. I then try to make the code as general as appropriate -- but no
more. I specifically avoid 100% solutions. Perhaps I should stop here
and try to give the technical answer to the question.

The first answer is that the problem is easier to solve with a version
of pre-post-order that passes a state: traversal in a state monad. I
have found such a version useful in some circumstances. As a matter
of fact, the imperative code above implements exactly that
approach. The mutable variable 'left' constitutes the state that is
_implicitly_ threaded through the traversal.

If we really want to use pre-post-order in a pure functional way, we
should probably make the modification to the source SXML. When
pre-post-order processes a nodelist, each node in the nodelist is
processed independently. If the dependency is desired, we should
either use the "second-order" pre-post-order (aka CPS pre-post-order:
cf. the emulation of left-fold via right-fold) or enclose all the 'td'
elements in a container. Frankly, none of these methods seem to be
satisfying: if we have a list of items and wish to group them in
pairs, perhaps the straightforward Scheme code would be the best
solution. 

Incidentally, if we rely on SXPath to produce the list of 'td' elements,
we can have SXPath produce the desired pairs:

(define doc
  '(doc
     (table
       (tr
	 (td "A1"))
       (tr (td "A2") (td "B1"))
       (thead)
       (tr (td "B2") (table (tr (td "C1") (td "C2"))))
       ) "text"))


  ((sxpath `(// td *text*
	      ,(let ((prev #f))
		(lambda (node)
		  (if prev (begin0 `(*pair ,prev ,node) (set! prev #f))
		    (begin (set! prev node) '()))))))
    doc)
===>
((*pair "A1" "A2")
 (*pair "B1" "B2")
 (*pair "C1" "C2"))


-------------------------------------------------------
This SF.Net email is sponsored by: IBM Linux Tutorials
Free Linux tutorial presented by Daniel Robbins, President and CEO of
GenToo technologies. Learn everything from fundamentals to system
administration.http://ads.osdn.com/?ad_id=1470&alloc_id=3638&op=click