rose_tree v0.2.0 RoseTree.Zipper
A zipper provides a mechanism for traversing a tree by focusing on a given node and maintaining enough data to reconstruct the overall tree from any given node.
Because most of the functions in RoseTree.Zipper
can attempt to access data that
may not exist, e.g. an out-of-bounds index in a node’s array of chlidren, the majority
of the functions return values with tagged tuples to let the user explicitly handle
success and error cases: {:ok, {%RoseTree{}, []}} | {:error, {:rose_tree, error_message}}
To make working with these values easier, the function lift/2
takes one of these tagged
tuple (either zipper | error
) values and a function. If the first argument is an error,
the error is passed through; if it is an {:ok, zipper}
tuple, the function is applied to
the zipper. This lets you easily chain successive calls to tree manipulation functions.
Adapted from Huet (1997); additional functionality inspired by Data.Tree.Zipper.
Link to this section Summary
Functions
Move up the tree to the parent of the node that the zipper is currently focused on
Move the zipper’s focus to the node’s first child that matches a predicate
Test whether the current focus is a the first (left-most) node among its siblings
Move the zipper’s focus to the node’s first child
Build a zipper focusing on the current tree
Test whether the current focus has any child nodes
Test whether the current focus has a parent node
Insert a tree as the first child of the current node. Focus moves to the newly inserted tree
Insert a tree as the last child of the current node. Focus moves to the newly inserted tree
Insert a tree to the left of the current focus. Focus moves to the newly inserted tree
Insert a tree as the nth child of the current node. Focus moves to the newly inserted tree
Insert a tree to the right of the current focus. Focus moves to the newly inserted tree
Test whether the current focus is a the last (right-most) node among its siblings
Move the zipper’s focus to the node’s last child
Test whether the current focus is a leaf of the tree
Lift a function expecting a zipper as an argument to one that can handle either {:ok, zipper} | {:error, error}
Apply a function to the value of a tree under the focus of a zipper
Move the zipper’s focus to the next sibling of the currently focused node
Move the zipper’s focus to the child at the given index
Test whether the current focus is a the only child of its parent
Move the zipper’s focus to the previous sibling of the currently focused node
Remove a subtree from a tree and move focus to the parent node
Test whether the current focus is the root of the tree
Nth_Child from the current focus to the left-most child tree
Ascend from any node to the root of the tree
Extract the currently focused tree from a zipper
Link to this section Types
breadcrumb() :: %{parent: any, left_siblings: [any], right_siblings: [any]}
Link to this section Functions
ascend(RoseTree.Zipper.t) :: {:ok, RoseTree.Zipper.t} | {:error, {:rose_tree, :no_parent}}
Move up the tree to the parent of the node that the zipper is currently focused on.
Examples
iex> zipper = RoseTree.new(:a, [:b, RoseTree.new(:c, [:d, :z])])
...> |> Zipper.from_tree()
...> {:ok, nth_child} = Zipper.nth_child(zipper, 0)
...> Zipper.ascend(nth_child)
{:ok,
{%RoseTree{node: :a, children: [
%RoseTree{node: :b, children: []},
%RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :z, children: []}
]}
]}, []}
}
find_child(RoseTree.Zipper.t, (any -> any)) :: {:ok, RoseTree.Zipper.t} | {:error, {:rose_tree, :no_child_match}}
Move the zipper’s focus to the node’s first child that matches a predicate.
Examples
iex> RoseTree.new(:a, [RoseTree.new(1, [15, 5])])
...> |> Zipper.from_tree()
...> |> Zipper.nth_child(0)
...> |> Zipper.lift(&Zipper.find_child(&1, fn(child) -> child.node > 10 end))
{:ok, {
%RoseTree{children: [], node: 15}, [
%{parent: 1, left_siblings: [], right_siblings: [%RoseTree{children: [], node: 5}]},
%{parent: :a, left_siblings: [], right_siblings: []}
]
}}
iex> RoseTree.new(:a, [RoseTree.new(1, [15, 5])])
...> |> Zipper.from_tree()
...> |> Zipper.nth_child(0)
...> |> Zipper.lift(&Zipper.find_child(&1, fn(child) -> length(child.children) > 5 end))
{:error, {:rose_tree, :no_child_match}}
Test whether the current focus is a the first (left-most) node among its siblings.
Examples
iex> RoseTree.new(:a, [:x, :y, :z])
...> |> Zipper.from_tree()
...> |> Zipper.first_child()
...> |> Zipper.lift(&Zipper.first?/1)
true
iex> RoseTree.new(:a, [:x, :y, :z])
...> |> Zipper.from_tree()
...> |> Zipper.first_child()
...> |> Zipper.lift(&Zipper.next_sibling/1)
...> |> Zipper.lift(&Zipper.next_sibling/1)
...> |> Zipper.lift(&Zipper.first?/1)
false
first_child(RoseTree.Zipper.t) :: {:ok, RoseTree.Zipper.t} | {:error, {:rose_tree, :no_children}}
Move the zipper’s focus to the node’s first child.
If the node is a leaf (and thus has no children), returns {:error, {:rose_tree, :no_children}}. Otherwise, returns {:ok, zipper}
Examples
iex> RoseTree.new(:a, [:b, RoseTree.new(:c, [:d, :z])])
...> |> Zipper.from_tree()
...> |> Zipper.first_child()
{:ok, {%RoseTree{node: :b, children: []}, [%{
parent: :a,
left_siblings: [],
right_siblings: [
%RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :z, children: []}
]}
]
}]}}
Build a zipper focusing on the current tree.
Examples
iex> RoseTree.new(:a, [:b, RoseTree.new(:c, [:d, :z])])
...> |> Zipper.from_tree()
{%RoseTree{node: :a, children: [
%RoseTree{node: :b, children: []},
%RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :z, children: []}
]}
]}, []}
Test whether the current focus has any child nodes.
Examples
iex> RoseTree.new(:a, :b)
...> |> Zipper.from_tree()
...> |> Zipper.has_children?()
true
iex> RoseTree.new(:a, :b)
...> |> Zipper.from_tree()
...> |> Zipper.first_child()
...> |> Zipper.lift(&Zipper.has_children?/1)
false
Test whether the current focus has a parent node.
Examples
iex> RoseTree.new(:a, :b)
...> |> Zipper.from_tree()
...> |> Zipper.first_child()
...> |> Zipper.lift(&Zipper.has_parent?/1)
true
iex> RoseTree.new(:a, :b)
...> |> Zipper.from_tree()
...> |> Zipper.has_parent?()
false
insert_first_child(RoseTree.Zipper.t, RoseTree.t) :: {:ok, RoseTree.Zipper.t}
Insert a tree as the first child of the current node. Focus moves to the newly inserted tree.
Example
iex> RoseTree.new(:a, [RoseTree.new(:b, [:y, :z]), RoseTree.new(:c, [:d, :e])])
...> |> Zipper.from_tree()
...> |> Zipper.insert_first_child(RoseTree.new(:x))
{:ok, {%RoseTree{node: :x, children: []}, [
%{parent: :a,
left_siblings: [],
right_siblings: [
%RoseTree{node: :b, children: [
%RoseTree{node: :y, children: []},
%RoseTree{node: :z, children: []}
]},
%RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :e, children: []}
]}
]
}
]}}
insert_last_child(RoseTree.Zipper.t, RoseTree.t) :: {:ok, RoseTree.Zipper.t}
Insert a tree as the last child of the current node. Focus moves to the newly inserted tree.
Example
iex> t = RoseTree.new(:a, [RoseTree.new(:b, [:y, :z]), RoseTree.new(:c, [:d, :e])])
...> |> Zipper.from_tree()
...> |> Zipper.insert_last_child(RoseTree.new(:x))
{:ok, {%RoseTree{node: :x, children: []}, [
%{parent: :a,
left_siblings: [
%RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :e, children: []}
]},
%RoseTree{node: :b, children: [
%RoseTree{node: :y, children: []},
%RoseTree{node: :z, children: []}
]}
],
right_siblings: []
}
]}}
iex> Zipper.lift(t, &Zipper.to_root/1)
...> |> Zipper.to_tree()
%RoseTree{node: :a,
children: [
%RoseTree{node: :b, children: [
%RoseTree{node: :y, children: []},
%RoseTree{node: :z, children: []}
]},
%RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :e, children: []}
]},
%RoseTree{node: :x, children: []}
]
}
insert_left(RoseTree.Zipper.t, RoseTree.t) :: either_zipper
Insert a tree to the left of the current focus. Focus moves to the newly inserted tree.
Example
iex> RoseTree.new(:a, [RoseTree.new(:b, [:y, :z]), RoseTree.new(:c, [:d, :e])])
...> |> Zipper.from_tree()
...> |> Zipper.first_child()
...> |> Zipper.lift(&Zipper.nth_child(&1, 1))
...> |> Zipper.lift(&Zipper.insert_left(&1, RoseTree.new(:x)))
{:ok, {%RoseTree{node: :x, children: []}, [
%{parent: :b,
left_siblings: [
%RoseTree{node: :y, children: []},
],
right_siblings: [
%RoseTree{node: :z, children: []},
]
},
%{parent: :a,
left_siblings: [],
right_siblings: [
%RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :e, children: []}
]}
]
}
]}}
insert_nth_child(RoseTree.Zipper.t, pos_integer, RoseTree.t) :: either_zipper
Insert a tree as the nth child of the current node. Focus moves to the newly inserted tree.
Example
iex> RoseTree.new(:a, [RoseTree.new(:b, [:y, :z]), RoseTree.new(:c, [:d, :e])])
...> |> Zipper.from_tree()
...> |> Zipper.insert_nth_child(1, RoseTree.new(:x))
{:ok, {%RoseTree{node: :x, children: []}, [
%{parent: :a,
left_siblings: [
%RoseTree{node: :b, children: [
%RoseTree{node: :y, children: []},
%RoseTree{node: :z, children: []}
]}
],
right_siblings: [
%RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :e, children: []}
]},
]
}
]}}
iex> RoseTree.new(:a, [RoseTree.new(:b, [:y, :z]), RoseTree.new(:c, [:d, :e])])
...> |> Zipper.from_tree()
...> |> Zipper.insert_nth_child(4, RoseTree.new(:x))
{:error, {:rose_tree, :bad_insertion_index}}
insert_right(RoseTree.Zipper.t, RoseTree.t) :: either_zipper
Insert a tree to the right of the current focus. Focus moves to the newly inserted tree.
Example
iex> RoseTree.new(:a, [RoseTree.new(:b, [:y, :z]), RoseTree.new(:c, [:d, :e])])
...> |> Zipper.from_tree()
...> |> Zipper.first_child()
...> |> Zipper.lift(&Zipper.nth_child(&1, 1))
...> |> Zipper.lift(&Zipper.insert_right(&1, RoseTree.new(:x)))
{:ok, {%RoseTree{node: :x, children: []}, [
%{parent: :b,
left_siblings: [
%RoseTree{node: :z, children: []},
%RoseTree{node: :y, children: []},
],
right_siblings: [
]
},
%{parent: :a,
left_siblings: [],
right_siblings: [
%RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :e, children: []}
]}
]
}
]}}
Test whether the current focus is a the last (right-most) node among its siblings.
Examples
iex> RoseTree.new(:a, [:x, :y, :z])
...> |> Zipper.from_tree()
...> |> Zipper.first_child()
...> |> Zipper.lift(&Zipper.next_sibling/1)
...> |> Zipper.lift(&Zipper.next_sibling/1)
...> |> Zipper.lift(&Zipper.last?/1)
true
iex> RoseTree.new(:a, [:x, :y, :z])
...> |> Zipper.from_tree()
...> |> Zipper.first_child()
...> |> Zipper.lift(&Zipper.last?/1)
false
last_child(RoseTree.Zipper.t) :: {:ok, RoseTree.Zipper.t} | {:error, {:rose_tree, :no_children}}
Move the zipper’s focus to the node’s last child.
If the node is a leaf (and thus has no children), returns {:error, {:rose_tree, :no_children}}. Otherwise, returns {:ok, zipper}
Examples
iex> RoseTree.new(:a, [:b, RoseTree.new(:c, [:d, :z])])
...> |> Zipper.from_tree()
...> |> Zipper.last_child()
{:ok, {
%RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :z, children: []}
]},
[%{parent: :a, left_siblings: [%RoseTree{node: :b, children: []}], right_siblings: []}]
}}
Test whether the current focus is a leaf of the tree.
Examples
iex> RoseTree.new(:a, :b)
...> |> Zipper.from_tree()
...> |> Zipper.first_child()
...> |> Zipper.lift(&Zipper.leaf?/1)
true
iex> RoseTree.new(:a, :b)
...> |> Zipper.from_tree()
...> |> Zipper.leaf?()
false
lift(either_zipper, (any -> any)) :: either_zipper
Lift a function expecting a zipper as an argument to one that can handle either {:ok, zipper} | {:error, error}.
If the first argument is an :error tuple, the error is passed through. Otherwise, the function is applied to the zipper in the :ok tuple.
Examples
iex> RoseTree.new(:a, [:b, RoseTree.new(:c, [:d, :z])])
...> |> Zipper.from_tree()
...> |> Zipper.nth_child(1)
...> |> Zipper.lift(&Zipper.nth_child(&1, 0))
...> |> Zipper.lift(&Zipper.to_tree(&1))
%RoseTree{node: :d, children: []}
iex> RoseTree.new(:a, [:b, RoseTree.new(:c, [:d, :z])])
...> |> Zipper.from_tree()
...> |> Zipper.nth_child(1)
...> |> Zipper.lift(&Zipper.nth_child(&1, 0))
...> |> Zipper.lift(&Zipper.nth_child(&1, 1))
{:error, {:rose_tree, :no_children}}
modify(RoseTree.Zipper.t, (any -> any)) :: RoseTree.Zipper.t
Apply a function to the value of a tree under the focus of a zipper.
Examples
iex> zipper = RoseTree.new(0, [1, RoseTree.new(10, [11, 12])])
...> |> Zipper.from_tree()
...> {:ok, nth_child} = Zipper.nth_child(zipper, 0)
...> Zipper.modify(nth_child, fn(x) -> x * 5 end)
{%RoseTree{node: 5, children: []}, [%{
parent: 0,
left_siblings: [],
right_siblings: [
%RoseTree{node: 10, children: [
%RoseTree{node: 11, children: []},
%RoseTree{node: 12, children: []}
]}
]
}]}
next_sibling(RoseTree.Zipper.t) :: {:ok, RoseTree.Zipper.t} | {:error, {:rose_tree, :no_siblings}} | {:error, {:rose_tree, :no_next_sibling}}
Move the zipper’s focus to the next sibling of the currently focused node.
Examples
iex> RoseTree.new(:a, [RoseTree.new(:c, [:d, :z])])
...> |> Zipper.from_tree()
...> |> Zipper.first_child()
...> |> Zipper.lift(&Zipper.first_child(&1))
...> |> Zipper.lift(&Zipper.next_sibling/1)
{:ok, {
%RoseTree{children: [], node: :z}, [
%{parent: :c, left_siblings: [%RoseTree{children: [], node: :d}], right_siblings: []},
%{parent: :a, left_siblings: [], right_siblings: []}
]
}}
iex> RoseTree.new(:a, [RoseTree.new(:c, [:d, :z])])
...> |> Zipper.from_tree()
...> |> Zipper.first_child()
...> |> Zipper.lift(&Zipper.nth_child(&1, 1))
...> |> Zipper.lift(&Zipper.next_sibling/1)
{:error, {:rose_tree, :no_next_sibling}}
nth_child(RoseTree.Zipper.t, integer) :: {:ok, RoseTree.Zipper.t} | {:error, {:rose_tree, :no_children}}
Move the zipper’s focus to the child at the given index.
Examples
iex> RoseTree.new(:a, [:b, RoseTree.new(:c, [:d, :z])])
...> |> Zipper.from_tree()
...> |> Zipper.nth_child(0)
{:ok, {%RoseTree{node: :b, children: []}, [%{
parent: :a,
left_siblings: [],
right_siblings: [
%RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :z, children: []}
]}
]
}]}}
Test whether the current focus is a the only child of its parent.
Examples
iex> RoseTree.new(:a, :b)
...> |> Zipper.from_tree()
...> |> Zipper.first_child()
...> |> Zipper.lift(&Zipper.only_child?/1)
true
iex> RoseTree.new(:a, [:x, :y, :z])
...> |> Zipper.from_tree()
...> |> Zipper.first_child()
...> |> Zipper.lift(&Zipper.only_child?/1)
false
previous_sibling(RoseTree.Zipper.t) :: {:ok, RoseTree.Zipper.t} | {:error, {:rose_tree, :no_siblings}} | {:error, {:rose_tree, :no_previous_sibling}}
Move the zipper’s focus to the previous sibling of the currently focused node.
Examples
iex> RoseTree.new(:a, [RoseTree.new(:c, [:d, :z])])
...> |> Zipper.from_tree()
...> |> Zipper.nth_child(0)
...> |> Zipper.lift(&Zipper.nth_child(&1, 1))
...> |> Zipper.lift(&Zipper.previous_sibling/1)
{:ok, {
%RoseTree{children: [], node: :d}, [
%{parent: :c, left_siblings: [], right_siblings: [%RoseTree{children: [], node: :z}]},
%{parent: :a, left_siblings: [], right_siblings: []}
]
}}
iex> RoseTree.new(:a, [RoseTree.new(:c, [:d, :z])])
...> |> Zipper.from_tree()
...> |> Zipper.nth_child(0)
...> |> Zipper.lift(&Zipper.nth_child(&1, 0))
...> |> Zipper.lift(&Zipper.previous_sibling/1)
{:error, {:rose_tree, :no_previous_sibling}}
Remove a subtree from a tree and move focus to the parent node.
Examples
iex> RoseTree.new(:a, [:b, RoseTree.new(:c, [:d, :z])])
...> |> Zipper.from_tree()
...> |> Zipper.nth_child(0)
...> |> Zipper.lift(&Zipper.prune(&1))
...> |> Zipper.to_tree()
%RoseTree{node: :a, children: [
%RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :z, children: []}
]}
]}
Test whether the current focus is the root of the tree.
Examples
iex> RoseTree.new(:a, :b)
...> |> Zipper.from_tree()
...> |> Zipper.root?()
true
iex> RoseTree.new(:a, :b)
...> |> Zipper.from_tree()
...> |> Zipper.first_child()
...> |> Zipper.lift(&Zipper.root?/1)
false
Nth_Child from the current focus to the left-most child tree.
If the focus is already on a leaf, it does not move.
Examples
iex> RoseTree.new(:a, [:b, RoseTree.new(:c, [:d, :z])])
...> |> Zipper.from_tree()
...> |> Zipper.to_leaf()
{%RoseTree{node: :b, children: []}, [
%{
parent: :a,
left_siblings: [],
right_siblings: [
%RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :z, children: []}
]}
]
}
]}
iex> RoseTree.new(:a, [RoseTree.new(:b, [:x, :y, :z]), RoseTree.new(:c, [:d, :e])])
...> |> Zipper.from_tree()
...> |> Zipper.to_leaf()
{%RoseTree{node: :x, children: []}, [
%{parent: :b,
left_siblings: [],
right_siblings: [
%RoseTree{node: :y, children: []},
%RoseTree{node: :z, children: []},
]
},
%{parent: :a,
left_siblings: [],
right_siblings: [
%RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :e, children: []}
]}
]
}
]}
Ascend from any node to the root of the tree.
If the focus is already on the root, it does not move.
Examples
iex> RoseTree.new(:a, [:b, RoseTree.new(:c, [:d, :z])])
...> |> Zipper.from_tree()
...> |> Zipper.nth_child(1)
...> |> Zipper.lift(&Zipper.nth_child(&1, 0))
...> |> Zipper.lift(&Zipper.to_root(&1))
{%RoseTree{node: :a, children: [
%RoseTree{node: :b, children: []},
%RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :z, children: []}
]}
]}, []}
iex> RoseTree.new(:a, [RoseTree.new(:b, [:x, :y, :z]), RoseTree.new(:c, [:d, :e])])
...> |> Zipper.from_tree()
...> |> Zipper.first_child()
...> |> Zipper.lift(&Zipper.nth_child(&1, 2))
...> |> Zipper.lift(&Zipper.to_root/1)
{%RoseTree{node: :a, children: [
%RoseTree{node: :b, children: [
%RoseTree{node: :x, children: []},
%RoseTree{node: :y, children: []},
%RoseTree{node: :z, children: []},
]},
%RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :e, children: []},
]},
]}, []}