How to move zipper to left/right node in clojure? - clojure

I'm writing a tree ( business process decision tree ) in clojure data structure .
(require clojure.zip :as z)
(z/vector-zip
[ :billed?
[:yes
[:check-bank-account]
[:check-cash] ]
[:send-out-email] ])
when code walk on the first node, it will read the keyword and perform certain actions, the result will be True or False, then I would like it walk into left ( True) or right (False) node .
When my code starts with root node, and call some function associated with :billed? it return a True, how could clojure walk into :yes node, or :send-out-email node ? I thought there is only z/down while, left or rightis only for siblings not for directions of children.
Thank you very much for your time and appreciate any thoughts

Zippers walk through a data structure while keeping track of the current position. To reach different nodes, you have to apply a sequence of moves to the same zipper. When the zipper is created your position is directly above the tree:
(z/node tree)
=> [:billed? [:yes [:check-bank-account] [:check-cash]] [:send-out-email]]
So you can descend into the tree with z/down, and use z/node to get the current node from the zipper's location:
(-> tree
z/down
z/node)
=> :billed?
If you're walking the tree from the top towards some node, you probably only need z/down and z/right, since descending into a child vector will place you at the leftmost child. It's easier to imagine this if you lay out the vector in a flat line and imagine z/right simply moving a cursor to the next element, and z/down moving the cursor to inside a vector.
(-> tree
z/down
z/right
z/node)
=> [:yes [:check-bank-account] [:check-cash]]
(-> tree
z/down
z/right
z/right
z/node)
=> [:send-out-email]
Here's an example of you could walk this tree by evaluating the keys against a map of facts:
(def tree
(z/vector-zip
[:billed?
[:wire-funds?
[:check-bank-account]
[:check-cash]]
[:send-out-email]]))
(defn facts->action [facts]
(loop [curr (z/down tree)]
(let [node (z/node curr)]
(if-let [fact (find facts node)]
(if (val fact)
(recur (-> curr z/right z/down)) ;; descend "left"
(recur (-> curr z/right z/right z/down))) ;; descend "right"
node))))
(facts->action {:billed? false})
=> :send-out-email
(facts->action {:billed? true :wire-funds? true})
=> :check-bank-account
(facts->action {:billed? true :wire-funds? false})
=> :check-cash

Chain multiple zipper movements.
Here's an example:
(require '[clojure.zip :as z])
(def zipper (z/vector-zip
[:billed?
[:yes
[:check-bank-account]
[:check-cash]]
[:send-out-email]]))
(println (-> zipper z/down z/right z/down z/node))
(println (-> zipper z/down z/right z/down z/right z/down z/node))

Related

What does the children function in tree-seq do

In clojure standard library there is function tree-seq which does depth-first traversal on a sequence.
(tree-seq branch? children root)
The branch parameter checks for terminal nodes, how ever what does the children function do?
According to the docs children must be a fn of one arg that returns a sequence of the children
In flatten, tree-seq is used as follows (tree-seq sequential? seq x). However if you do (seq 1) then it throws an IllegalArgumentException.
When and how is the children function used?
tree-seq's branch? function checks if a parameter is a branch, not if it's a terminal node. A branch has leaf nodes, a terminal node is a leaf node.
If we have a branch the children functions should return the wanted leaf nodes.
In the case of flatten's (tree-seq sequential? seq x) a branch is checked with sequential? and children of a branch are returned via seq.
E.g., (sequential? [1 2 3 4]) ;; => true, and the children via (seq [1 2 3 4]) ;; => (1 2 3 4). And for a leaf node (sequential? 1) => false, and we terminate.

Is manipulating a vector of nested maps possible using zippers?

I need to turn the following input into output by applying the following two rules:
remove all vectors that have "nope" as last item
remove each map that does not have at least one vector with "ds1" as last item
(def input
[{:simple1 [:from [:simple1 'ds1]]}
{:simple2 [:from-any [[:simple2 'nope] [:simple2 'ds1]]]}
{:walk1 [:from [:sub1 :sub2 'ds1]]}
{:unaffected [:from [:unaffected 'nope]]}
{:replaced-with-nil [:from [:the-original 'ds1]]}
{:concat1 [:concat [[:simple1 'ds1] [:simple2 'ds1]]]}
{:lookup-word [:lookup [:word 'word :word 'ds1]]}])
(def output
[{:simple1 [:from [:simple1 'ds1]]}
{:simple2 [:from-any [[:simple2 'ds1]]]}
{:walk1 [:from [:sub1 :sub2 'ds1]]}
{:replaced-with-nil [:from [:the-original 'ds1]]}
{:concat1 [:concat [[:simple1 'ds1] [:simple2 'ds1]]]}
{:lookup-word [:lookup [:word 'word :word 'ds1]]}])
I was wondering if performing this transformation is possible with zippers?
I'd recommend clojure.walk instead for this kind of general tree transformation. It can take a bit of fiddling to get the replacement functions right but it works nicely with any nesting of Clojure data structures, which AFAIK can be a bit more challenging in a zipper based approach.
We're looking to shrink our tree, so postwalk is my go-to here. It takes a function f and a tree root and goes through the tree, replacing each leaf value with (f leaf), then their parents and their parents etc. until finally replacing the root. (prewalk is similar but proceeds from root and down to leaves, so it's usually more natural when you're growing the tree by splitting branches.)
The strategy here is to somehow construct a function that prunes any branch which meets our removal criteria, but returns any other value unchanged.
(ns shrink-tree
(:require [clojure.walk :refer [postwalk]]))
(letfn[(rule-1 [node]
(and (vector? node)
(= 'nope (last node))))
(rule-2 [node]
(and
(map? node)
(not-any? #(and (vector? %) (= 'ds1 (last %)))
(tree-seq vector? seq (-> node vals first)))))
(remove-marked [node]
(if (coll? node)
(into (empty node) (remove (some-fn rule-1 rule-2) node))
node))]
(= output (postwalk remove-marked input)))
;; => true
Here the fns rule-1 and rule-2 try to turn your rules into predicates and remove-marked:
If a node is a collection, returns the same collection, less any members for which rule1 or rule2 return truthy when called with that member. To check for either one at the same time we combine the predicates with some-fn.
Otherwise returns the same node. This is how we keep values like 'ds1 or :from-any around.
You might also want to consider looking at specter. It supports these sorts of transformations by allowing you to select and transform arbitrarily complex structures.

Clojure (or any functional language): is there a functional way of building flat lists by a recursive function?

I've got a recursive function building a list:
(defn- traverse-dir
"Traverses the (source) directory, preorder"
[src-dir dst-root dst-step ffc!]
(let [{:keys [options]} *parsed-args*
uname (:unified-name options)
[dirs files] (list-dir-groomed (fs/list-dir src-dir))
... recursive call of traverse-dir is the last expression of dir-handler
(doall (concat (map-indexed (dir-handler) dirs) (map-indexed (file-handler) files))))) ;; traverse-dir
The list, built by traverse-dir, is recursive, while I want a flat one:
flat-list (->> (flatten recursive-list) (partition 2) (map vec))
Is there a way of building the flat list in the first place? Short of using mutable lists, that is.
I don't quite understand your context with a dir-handler that is called with nothing and returns a function which expects indices and directories, list-dir-groomed and all of that, but I'd recommend a look at tree-seq:
(defn tree-seq
"Returns a lazy sequence of the nodes in a tree, via a depth-first walk.
branch? must be a fn of one arg that returns true if passed a node
that can have children (but may not). children must be a fn of one
arg that returns a sequence of the children. Will only be called on
nodes for which branch? returns true. Root is the root node of the
tree."
{:added "1.0"
:static true}
[branch? children root]
(let [walk (fn walk [node]
(lazy-seq
(cons node
(when (branch? node)
(mapcat walk (children node))))))]
(walk root)))
My go-to use here is
(tree-seq #(.isDirectory %) #(.listFiles %) (clojure.java.io/as-file file-name))
but your context might mean that doesn't work. You can change to different functions for getting child files if you need to sanitize those, or you can just use filter on the output. If that's no good, the same pattern of a local fn from nodes into pre-walks that handles children by recursively mapcatting itself over them seems pretty applicable.

Get the source of clojure.zip

Obviously, I can go to the Clojure's Github page or look in my maven repository, this is not a question about literally getting the source code.
I want to programmatically get all the top level forms defined in a namespace. Something like
(get-top-level-forms-of 'clojure.zip)
=>
['(ns ^{:doc "Functional hierarchical zipper, with navigation, editing,
and enumeration. See Huet"
:author "Rich Hickey"}
clojure.zip
(:refer-clojure :exclude (replace remove next)))
'(defn zipper
"Creates a new zipper structure.
branch? is a fn that, given a node, returns true if can have
children, even if it currently doesn't.
children is a fn that, given a branch node, returns a seq of its
children.
make-node is a fn that, given an existing node and a seq of
children, returns a new branch node with the supplied children.
root is the root node."
{:added "1.0"}
[branch? children make-node root]
^{:zip/branch? branch? :zip/children children :zip/make-node make-node}
[root nil])
'(defn seq-zip
"Returns a zipper for nested sequences, given a root sequence"
{:added "1.0"}
[root]
(zipper seq?
identity
.....__ALL_THE_REST_OF_THE_FORMS_IN_clojure.zip_....]
Basically, just getting a ordered sequence of all the forms in a namespace, in the order they were defined. Is this possible?
This will extract top-level forms from the copy of the clojure.zip source bundled inside the Clojure jar:
(require '[clojure.java.io :as io])
(let [rdr (clojure.lang.LineNumberingPushbackReader.
(io/reader (io/resource "clojure/zip.clj")))
sentinel (Object.)]
(take-while #(not (identical? sentinel %))
(repeatedly #(read rdr false sentinel))))
;= ((ns clojure.zip (:refer-clojure :exclude (replace remove next))) ...)
That should do the job:
(keys (ns-publics 'clojure.zip))
Which returns:
(lefts down insert-left up next path children vector-zip append-child zipper branch? end? leftmost edit replace insert-right root insert-child prev seq-zip xml-zip make-node rights node right left remove rightmost)
Other ns-* functions can be found in the namespaces page.

How do you make a binary search tree in Clojure?

In Scheme, I can use define-struct to make a binary search tree, but how do you do it in Clojure?
You can use structmaps. To define one:
(defstruct bintree :left :right :key)
To make an instance:
(struct-map bintree :left nil :right nil :key 0)
You can then access the values in the struct like this:
(:left tree)
etc.
Or you can create new accessor functions:
(def left-branch (accessor bintree :left))
and use it:
(left-branch tree)
I don't know Clojure, but I bet it's the same way you do it in Scheme without define-struct ... just cons together the left and right branches. To find something, recurse until you hit an atom.
Seriously, though, structmaps sound like what you want. I found this page. Look for structmaps about half way down.
The simplest way would be to use the tree that is already defined in language (every sorted-map is a tree really, if you just need different function to compare keys, use sorted-map-by).
;;define function for comparing keys
(defn compare-key-fn [key1 key2] (< key1 key2) )
;;define tree and add elements
(def my-tree
(-> ;;syntax sugar
(sorted-map-by compare-key-fn) ;;this returns empty tree with given function to compare keys
(assoc 100 "data for key = 100") ;;below we add elements to tree
(assoc 2 "data for key = 2")
(assoc 10 "data for key = 10")
(assoc -2 "data for key = -1")))
;;accesing elements by key
(prn "element for key 100 =" (my-tree 100))
;;"erasing" elements from tree - in reality, what we are really doing, is returning a new tree that contains all elements of the old one, except the element we've just erased.
(def my-new-tree
(dissoc my-tree 2))
(prn my-new-tree) ;; to verify, that element 2 is "erased"