On Thu, Jul 8, 2010 at 3:40 AM, Brian Theado <[email protected]> wrote:

> I've seen Ville's 'find_b' and 'find_h' which allows
> xpath-like/jquery-like chainable searches of Leo body  and head
> strings.  That functionality does not deal much with tree
> structure--it always searches child nodes.  That is useful, but
> sometimes more flexible traversal of tree structure is needed. I have
> written some code that provides this flexible traversal.

These methods could also be added to 'poslist' class that you get from
find_h / find_b. LeoTreeql is basically the same thing with the
difference that it doesn't inherit from list.


>
> Leo already has many tree iterators and in fact my code is mostly just
> a thin wrapper around the already existing leo iterators.  However,
> the chainability is a nice touch that makes tree traversal tasks more
> pleasant.
>
> The code is kind of long for an email, but I've included it
> nonetheless.  Example uses at the bottom, but here is my favorite
> example:
>
> g.es ('\nParents of all the clones of the current position')
> map (g.es, LeoTreeql(p).clones().parent().it())
>
> The rest of the code:
>
> import itertools
>
> class LeoTreeql:
>    def __init__(self,p):
>        # The initial iterable is just the given p as a list.
>        # With each step in the chain, this iterable will be
>        # refined as a composition of the new iterable upon the
>        # results of the old iterable
>        self.i = [p]
>        self.p = p
>
>    def refine (self,f):
>        # Here is the main engine which applies the new iterable to the
>        # results of the previously stored iterable.  It took quite a bit of
>        # experimentation to arrive at this and I don't fully understand
>        # it to know whether it could be made clearer.  From my limited
>        # testing, it seems to work.
>        self.i = itertools.chain.from_iterable (itertools.imap (f, self.i))
>        return
>
>    def descendants (self):
>        # Pass in the "subtree" method of the position class as the
>        # refining iterable
>        self.refine(type(self.p).subtree)
>        return self
>
>    def children (self):
>        self.refine(type(self.p).children)
>        return self
>
>    def ancestors (self):
>        self.refine(type(self.p).parents)
>        return self
>
>    def parent (self):
>        # Returns just a single parent, but each step must be an
>        # iterable, so make one.
>        def parent_iter(p0):
>            yield p0.parent()
>            raise StopIteration
>        self.refine(parent_iter)
>        return self
>
>    def following_siblings (self):
>        self.refine(type(self.p).following_siblings)
>        return self
>
>    def self_and_siblings (self):
>        self.refine(type(self.p).self_and_siblings)
>        return self
>
>    def root (self):
>        # This is a strange one.  Leo's root is not the parent of all toplevel
>        # nodes, it is simply the first toplevel node.  So walking the whole
>        # tree is not q.root().descendants() and instead is
>        # q.root().following_siblings().descendants()
>        # Strange only because that is a different definition of root than
>        # I've seen before
>        def root_iter(p0):
>            yield p0.findRootPosition()
>        self.refine(root_iter)
>        return self
>
>    def clones(self):
>        # My own all_positions that doesn't depend on 'c', since this
> class only knows about 'p'
>        def all_positions (p0):
>            p = p0.findRootPosition()
>            while p:
>                yield p
>                p.moveToThreadNext()
>            raise StopIteration
>
>        def clones_iter(p0):
>            def isCloneOf(p1,p2=p0):
>                return p1.v == p2.v
>
>            # Finding clones of a node involves searching the whole tree
>            return itertools.ifilter (isCloneOf, all_positions(p0))
>
>        self.refine(clones_iter)
>        return self
>
>    # This is the "escape hatch" from the chaining.  This method will
>    # typically be the last call in the chain
>    def it(self):
>        return self.i
>
> # Looks to be working for at least a few cases
> g.es('=======')
>
> g.es('\nChildren, nieces, and nephews')
> map (g.es, LeoTreeql(p).self_and_siblings().children().it())
>
> g.es('\nAncestors of descendants of brothers and sisters')
> map (g.es, LeoTreeql(p).self_and_siblings().descendants().ancestors().it())
>
> g.es('\nChildren of parents (should be the same as self and parents)')
> map (g.es, LeoTreeql(p).parent().children().it())
>
> g.es('\nChildren of root (i.e. toplevel nodes except the first one)')
> map (g.es, LeoTreeql(p).root().following_siblings().it())
>
> g.es ('\nParents of all the clones of the current position')
> map (g.es, LeoTreeql(p).clones().parent().it())
>
> g.es ('\nCousins of current position and siblings and self')
> map (g.es, LeoTreeql(p).parent().self_and_siblings().children().it())
>
> g.es ('\nmultiple levels of empty iterators should be handled fine
> (just an empty result):')
> map (g.es, LeoTreeql(p).children().children().children().it())
>
> Brian
>
> --
> You received this message because you are subscribed to the Google Groups 
> "leo-editor" group.
> To post to this group, send email to [email protected].
> To unsubscribe from this group, send email to 
> [email protected].
> For more options, visit this group at 
> http://groups.google.com/group/leo-editor?hl=en.
>
>



-- 
Ville M. Vainio
http://tinyurl.com/vainio

-- 
You received this message because you are subscribed to the Google Groups 
"leo-editor" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/leo-editor?hl=en.

Reply via email to