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.