Re: Counting Nodes

Subject: Re: Counting Nodes
From: Brandon Ibach <bibach@xxxxxxxxxxxxxx>
Date: Fri, 25 Feb 2000 12:20:04 -0600
Quoting Maltby, David G <david.g.maltby@xxxxxxxx>:
> With Brandon's theory that non-element class nodes were being included in
> the node-list returned by (siblings) proven correct, I have rewritten my
> procedure to trap those nodes and not count them.  This procedure provides
> the functionality I need.
> 
> [...]
> 
> Is there are better or more appropriate element class test of the node then
> (gi)?  Is there efficiencies to be gained by naming (node-list-first
> text-siblings) rather then doing the (node-list-first ) call three times?
> 
   Well, you could do: (equal? 'element (node-property 'classnm n)),
but I expect that (gi) is just fine, too, at least in this case, and
as you can see in Matthias' new code, it allows you to check even more
specifically, just in case <text> changes later to allow other
elements in its content model.
   As for calling (node-list-first) three times, you *could* wrap a
(let) around your (cond), and do it just once at the top, but I'd
guess, offhand, that the three calls aren't really creating that much
overhead.

> I need to understand mapping a procedure over a list better.
> 
   Well, here's a pretty good opportunity, because your procedure and
Matthias' are fairly similar in their end result.  Let's compare... :)

Quoting Matthias Clasen <clasen@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>:
> (define (text-node-cnt #!optional (cur-text-node (current-node)))
>   (node-list-length
>      (node-list-filter
>        (lambda (snl) (and 
>                        (equal? (gi snl) "TEXT") 
>                        (not (attribute-string "type" snl))))
>        (preced cur-text-node))))
> 
   Matthias is using (node-list-filter) here.  According to the
standard (combining the definitions of node-list-filter and
node-list-reduce a bit), (node-list-filter) can be defined as:

(define node-list-filter (lambda (proc nl)
  (let loop ((n nl) (r (empty-node-list)))
    (cond ((node-list-empty? n) r)
          ((proc (node-list-first n))
                (loop (node-list-rest n) (node-list r (node-list-first n))))
          (else (loop (node-list-rest n) r))))))

   As you can see, (node-list-filter) just works its way through the
node-list, applying the procedure to each node.  If the procedure
evaluates to true, it adds the node to the result node-list.  Or,
otherwise put, it removes the nodes for which proc returns false.
   Now, Matthias also used (preced) to get his node-list, rather than
using (siblings).  Based on the version in the standard (but stripped
down to handle just a singleton node-list argument), (preced) is:

(define preced (lambda (snl)
  (let loop ((scanned (empty-node-list)) (rest (siblings snl)))
    (cond ((node-list-empty? rest)                  (empty-node-list))
          ((node-list=? snl (node-list-first rest)) scanned)
          (else   (loop (node-list scanned (node-list-first rest))
                        (node-list-rest rest)))))))

   Again, we're starting with the node-list from (siblings) and
filtering it.  If you look closely, you'll see that the first two
clauses of the (cond) are essentially the same as the ones in your
procedure.  So, Matthias just took the shortcut on those and let a
built-in function do the work.
   If you put these two definitions ((preced) and (node-list-filter))
together, along with the procedure that Matthias passed in to
(node-list-filter), you should see that it's all basically the same as
your own procedure.  The difference is, Matthias' code was building a
node-list which we would calculate the length of at the end, where
your code was just incrementing a counter as it went along.

-Brandon :)


 DSSSList info and archive:  http://www.mulberrytech.com/dsssl/dssslist


Current Thread