A set of generic functions for traversing tree-like data structures recursively
and/or iteratively, ported
from clojure.walk
and clojure.zip
respectively.
treepy supports Emacs 25.1+.
It is available in MELPA, which is the recommended way to install it and keep it up to date.
To install it, you may do
M-x package-install RET treepy RET
If the installation doesn't work, consider refreshing the package list: M-x package-refresh-contents [RET]
For a manual installation, just place treepy.el in your load-path and
(require 'treepy). Then you'll have all the treepy-* functions available.
-
treepy-walkinner outer formTraverses FORM, an arbitrary data structure. INNER and OUTER are functions. Applies INNER to each element of FORM, building up a data structure of the same type, then applies OUTER to the result. Recognizes cons, lists, alists, vectors and hash tables.
-
treepy-postwalkf formPerforms a depth-first, post-order traversal of F applied to FORM. Calls F on each sub-form, uses F's return value in place of the original. Recognizes cons, lists, alists, vectors and hash tables.
-
treepy-prewalkf formLike
treepy-postwalk, Performs function F on FORM but does pre-order traversal. -
treepy-prewalk-demoformDemonstrates the behavior of
treepy-prewalkfor FORM. Returns a list of each form as it is walked. -
treepy-postwalk-demoformDemonstrates the behavior of
treepy-postwalkfor FORM. Returns a list of each form as it is walked. -
treepy-postwalk-replacesmap form &optional testfnRecursively transforms FORM by replacing keys in SMAP with their values. Does replacement at the root of the tree first. The optional TESTFN is passed to
map-contains-keyas the testing equality function. -
treepy-postwalk-replacesmap form &optional testfnRecursively transforms FORM by replacing keys in SMAP with their values. Does replacement at the leaves of the tree first. The optional TESTFN is passed to
map-contains-keyas the testing equality function.
-
treepy-zipperbranchp children make-node rootCreates a new zipper structure.
BRANCHP is a function that, given a node, returns t if it can have children, even if it currently doesn't.
CHILDREN is a function that, given a branch node, returns a seq of its children.
MAKE-NODE is a function that, given an existing node and a seq of children, returns a new branch node with the supplied children.
ROOT is the root node.
-
treepy-list-ziprootReturns a zipper for nested lists, given a ROOT list.
-
treepy-vector-ziprootReturns a zipper for nested vectors, given a ROOT vector.
-
treepy-nodelocReturns the node at LOC.
-
treepy-branch-plocReturns
tif the node at LOC is a branch.nilotherwise. -
treepy-childrenlocReturns a children list of the node at LOC, which must be a branch.
-
treepy-make-nodeloc node childrenReturns a new branch node, given an existing LOC, NODE and new CHILDREN. The LOC is only used to supply the constructor.
-
treepy-pathlocReturns a list of nodes leading to the given LOC.
-
treepy-leftslocReturns a list of the left siblings of this LOC.
-
treepy-rightslocReturns a list of the right siblings of this LOC.
-
treepy-downlocReturns the loc of the leftmost child of the node at this LOC, or
nilif no children. -
treepy-uplocReturns the loc of the parent of the node at this LOC, or
nilif at the top. -
treepy-rootlocZips from LOC all the way up and return the root node, reflecting any changes.
-
treepy-rightlocReturns the loc of the right sibling of the node at this LOC, or
nil. -
treepy-rightmostlocReturns the loc of the rightmost sibling of the node at this LOC, or self.
-
treepy-leftlocReturns the loc of the left sibling of the node at this LOC, or
nil. -
treepy-leftmostlocReturns the loc of the leftmost sibling of the node at this LOC, or self.
-
treepy-leftmost-descendantlocReturns the leftmost descendant of the given LOC. (ie, down repeatedly).
-
treepy-insert-leftloc itemInserts ITEM as the left sibling of this LOC'S node, without moving.
-
treepy-insert-rightloc itemInserts ITEM as the right sibling of this LOC's node, without moving.
-
treepy-replaceloc nodeReplaces the node in this LOC with the given NODE, without moving.
-
treepy-editloc f &rest argsReplaces the node at this LOC with the value of
(F node ARGS). -
treepy-insert-childloc itemInserts ITEM as the leftmost child of this LOC's node, without moving.
-
treepy-append-childloc itemInserts ITEM as the rightmost child of this LOC'S node, without moving.
-
treepy-removelocRemoves the node at LOC. Returns the loc that would have preceded it in a depth-first walk.
-
treepy-nextloc &optional orderMoves to the next LOC in the hierarchy, depth-first, using ORDER if given. Possible values for ORDER are
:preorderand:postorder, defaults to the former. When reaching the end, returns a distinguished loc detectable viatreepy-end-p. If already at the end, stays there. -
treepy-nextloc &optional orderMoves to the previous LOC in the hierarchy, depth-first, using ORDER if given. Possible values for ORDER are
:preorderand:postorder, defaults to the former. -
treepy-end-plocReturns
tif LOC represents the end of a depth-first walk,nilotherwise.
Even though one of treepy's goals is to provide an API that's as close as
possible
to clojure.walk
and clojure.zip,
there are some subtle (and not so subtle) differences derived from elisp/clojure
distinct data structures, levels of abstraction, and code conventions.
The most notorious difference is the name of the functions. For every function
in Clojure world, there's a treepy counterpart that's prefixed with treepy-.
So:
clojure.walk/walk->treepy-walkclojure.zip/zipper->treepy-zipper
... and so on.
-
treepy-walk(and all its derivatives) works on lists, vectors, alists and hash-tables only. -
Instead of printing to stdout,
treepy-prewalk-demoandtreepy-postwalk-demoreturn a list of the sub forms as they get walked. -
treepy doesn't provide implementations for
keywordize-keys,stringify-keysandmacroexpand-all. There's already amacroexpand-allimplementation built in. -
treepy-prewalk-replaceandtreepy-postwalk-replaceare based on the (Emacs 25) built inmap-contains-keyfunction. Both functions take an optional a testfn third parameter to be used bymap-contains-key. Defaults toequal.
-
In order to follow elisp conventions, treepy has a couple of other small renamings:
clojure.zip/branch?->treepy-branch-pclojure.zip/end?->treepy-end-p
-
There's is no exact equivalent to
clojure.zip/seq-zipin treepy, atreepy-list-zipis provided instead. -
treepy provides a way to traverse a tree in postorder while using the enumeration functions
treepy-nextandtreepy-prev. This is done by having an extra optional parameter order that can be passed to both functions:
(treepy-next loc) ;; => next node in preorder, as in clojure.zip/next.
(treepy-next loc :preorder) ;; => also next node in preorder.
(treepy-next loc :postorder) ;; => next node in postorder.
- There's a new function in treepy to get the leftmost descendant of a
node/loc. Unsurprisingly, it's called
treepy-leftmost-descendant. This function is particularly useful when trying to traverse a tree in post order, since unlike preorder trasversal, the root is NOT the first element you want walk/visit. You might want to call(treepy-leftmost-descendant root)before starting to walk the nodes withtreepy-nextin postorder.
The following are some treepy's implementation differences that you might not need to bother with if you just wanna use the library.
- treepy's loc data structure: When you create a Clojure "zipper" with
clojure.zip/zipper, you have to provide three helper functions (branch?,children, andmake-node) and arootform.clojure.zip/zipperwill then return a tuple vector that represents a "locaction". The three helper functions are stored as clojure's metadata into the returned loc. Since there's no equivalent to metadata in Elisp, treepy directly associates the three helper functions into an alist that's returned with the rest of the loc information. The resulting structure of a treepylocis the following:
((<current node> . <path alist>) . ((:branch-p . #'provided-branch-fn)
(:children . #'provided-children-fn)
(:make-node . #'provided-make-node-fn)))
<path alist>is an alist containing the same keys and values asclojure.zip's path map. Only difference is that treepy uses lists instead of vectors to handle theleftsiblings andpnodesparent nodes.
-
xiongtx/zipper.el: Non-generic, EIEIO, zipper implementation.
-
danielfm/cl-zipper: Common Lisp zipper implementation.
© 2017 Daniel Barreto
Distributed under the terms of the GNU GENERAL PUBLIC LICENSE, version 3.