rose_tree v0.1.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.
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 child at the index
Move the zipper’s focus to the node’s first child that matches a predicate
Move the zipper’s focus to the node’s first child
Build a zipper focusing on the current tree
Move the zipper’s focus to the node’s last child
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 previous sibling of the currently focused node
Remove a subtree from a tree and move focus to the parent node
Ascend from any node to the root of the tree
Extract the currently focused tree from a zipper
Link to this section Types
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> {:ok, tree} = with {:ok, b} <- RoseTree.new(:b),
...> {:ok, d} <- RoseTree.new(:d),
...> {:ok, z} <- RoseTree.new(:z),
...> {:ok, c} <- RoseTree.new(:c, [d, z]) do
...> RoseTree.new(:a, [b, c])
...> end
...> {:ok, descended} = Zipper.descend({tree, []}, 0)
...> Zipper.ascend(descended)
{:ok,
{%RoseTree{node: :a, children: [
%RoseTree{node: :b, children: []},
%RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :z, children: []}
]}
]}, []}
}
descend(RoseTree.Zipper.t, integer) :: {:ok, RoseTree.Zipper.t} | {:error, {:rose_tree, :no_children}}
Move the zipper’s focus to the child at the index.
Examples
iex> {:ok, tree} = with {:ok, b} <- RoseTree.new(:b),
...> {:ok, d} <- RoseTree.new(:d),
...> {:ok, z} <- RoseTree.new(:z),
...> {:ok, c} <- RoseTree.new(:c, [d, z]) do
...> RoseTree.new(:a, [b, c])
...> end
...> Zipper.descend({tree, []}, 0)
{:ok, {%RoseTree{node: :b, children: []}, [%{
node: :a,
index: 0,
other_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> {:ok, tree} = with {:ok, d} <- RoseTree.new(15),
...> {:ok, z} <- RoseTree.new(5),
...> {:ok, c} <- RoseTree.new(1, [d, z]) do
...> RoseTree.new(:a, [c])
...> end
...> d_focus = tree
...> |> Zipper.from_tree()
...> |> Zipper.descend(0)
...> |> Zipper.lift(&Zipper.find_child(&1, fn(child) -> child.node > 10 end))
{:ok, {
%RoseTree{children: [], node: 15}, [
%{index: 0, node: 1, other_children: [%RoseTree{children: [], node: 5}]},
%{index: 0, node: :a, other_children: []}
]
}}
iex> {:ok, tree} = with {:ok, d} <- RoseTree.new(15),
...> {:ok, z} <- RoseTree.new(5),
...> {:ok, c} <- RoseTree.new(1, [d, z]) do
...> RoseTree.new(:a, [c])
...> end
...> d_focus = tree
...> |> Zipper.from_tree()
...> |> Zipper.descend(0)
...> |> Zipper.lift(&Zipper.find_child(&1, fn(child) -> length(child.children) > 5 end))
{:error, {:rose_tree, :no_child_match}}
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> {:ok, tree} = with {:ok, b} <- RoseTree.new(:b), …> {:ok, d} <- RoseTree.new(:d), …> {:ok, z} <- RoseTree.new(:z), …> {:ok, c} <- RoseTree.new(:c, [d, z]) do …> RoseTree.new(:a, [b, c]) …> end …> tree …> |> Zipper.from_tree() …> |> Zipper.first_child() {:ok, {%RoseTree{node: :b, children: []}, [%{ node: :a, index: 0, other_children: [
%RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :z, children: []}
]}
] }]}}
Build a zipper focusing on the current tree.
Examples
iex> {:ok, tree} = with {:ok, b} <- RoseTree.new(:b),
...> {:ok, d} <- RoseTree.new(:d),
...> {:ok, z} <- RoseTree.new(:z),
...> {:ok, c} <- RoseTree.new(:c, [d, z]) do
...> RoseTree.new(:a, [b, c])
...> end
...> Zipper.from_tree(tree)
{%RoseTree{node: :a, children: [
%RoseTree{node: :b, children: []},
%RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :z, children: []}
]}
]}, []}
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> {:ok, tree} = with {:ok, b} <- RoseTree.new(:b), …> {:ok, d} <- RoseTree.new(:d), …> {:ok, z} <- RoseTree.new(:z), …> {:ok, c} <- RoseTree.new(:c, [d, z]) do …> RoseTree.new(:a, [b, c]) …> end …> tree …> |> Zipper.from_tree() …> |> Zipper.last_child() {:ok, { %RoseTree{node: :c, children: [
%RoseTree{node: :d, children: []},
%RoseTree{node: :z, children: []}
]}, [%{node: :a, index: 1, other_children: [%RoseTree{node: :b, children: []}]}] }}
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> {:ok, tree} = with {:ok, b} <- RoseTree.new(:b),
...> {:ok, d} <- RoseTree.new(:d),
...> {:ok, z} <- RoseTree.new(:z),
...> {:ok, c} <- RoseTree.new(:c, [d, z]) do
...> RoseTree.new(:a, [b, c])
...> end
...> tree
...> |> Zipper.from_tree()
...> |> Zipper.descend(1)
...> |> Zipper.lift(&Zipper.descend(&1, 0))
...> |> Zipper.lift(&Zipper.to_tree(&1))
%RoseTree{node: :d, children: []}
...> tree
...> |> Zipper.from_tree()
...> |> Zipper.descend(1)
...> |> Zipper.lift(&Zipper.descend(&1, 0))
...> |> Zipper.lift(&Zipper.descend(&1, 1))
{:error, {:rose_tree, :bad_path}}
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> {:ok, tree} = with {:ok, b} <- RoseTree.new(1), …> {:ok, d} <- RoseTree.new(11), …> {:ok, z} <- RoseTree.new(12), …> {:ok, c} <- RoseTree.new(10, [d, z]) do …> RoseTree.new(0, [b, c]) …> end …> {:ok, descended} = Zipper.descend({tree, []}, 0) …> Zipper.modify(descended, fn(x) -> x * 5 end) {%RoseTree{node: 5, children: []}, [%{
node: 0,
index: 0,
other_children: [
%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> {:ok, tree} = with {:ok, d} <- RoseTree.new(:d),
...> {:ok, z} <- RoseTree.new(:z),
...> {:ok, c} <- RoseTree.new(:c, [d, z]) do
...> RoseTree.new(:a, [c])
...> end
...> tree
...> |> Zipper.from_tree()
...> |> Zipper.descend(0)
...> |> Zipper.lift(&Zipper.descend(&1, 0))
...> |> Zipper.lift(&Zipper.next_sibling/1)
{:ok, {
%RoseTree{children: [], node: :z}, [
%{index: 1, node: :c, other_children: [%RoseTree{children: [], node: :d}]},
%{index: 0, node: :a, other_children: []}
]
}}
iex> {:ok, tree} = with {:ok, d} <- RoseTree.new(:d),
...> {:ok, z} <- RoseTree.new(:z),
...> {:ok, c} <- RoseTree.new(:c, [d, z]) do
...> RoseTree.new(:a, [c])
...> end
...> tree
...> |> Zipper.from_tree()
...> |> Zipper.descend(0)
...> |> Zipper.lift(&Zipper.descend(&1, 1))
...> |> Zipper.lift(&Zipper.next_sibling/1)
{:error, {:rose_tree, :no_next_sibling}}
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> {:ok, tree} = with {:ok, d} <- RoseTree.new(:d),
...> {:ok, z} <- RoseTree.new(:z),
...> {:ok, c} <- RoseTree.new(:c, [d, z]) do
...> RoseTree.new(:a, [c])
...> end
...> d_focus = tree
...> |> Zipper.from_tree()
...> |> Zipper.descend(0)
...> |> Zipper.lift(&Zipper.descend(&1, 1))
...> |> Zipper.lift(&Zipper.previous_sibling/1)
{:ok, {
%RoseTree{children: [], node: :d}, [
%{index: 0, node: :c, other_children: [%RoseTree{children: [], node: :z}]},
%{index: 0, node: :a, other_children: []}
]
}}
iex> {:ok, tree} = with {:ok, d} <- RoseTree.new(:d),
...> {:ok, z} <- RoseTree.new(:z),
...> {:ok, c} <- RoseTree.new(:c, [d, z]) do
...> RoseTree.new(:a, [c])
...> end
...> d_focus = tree
...> |> Zipper.from_tree()
...> |> Zipper.descend(0)
...> |> Zipper.lift(&Zipper.descend(&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> {:ok, tree} = with {:ok, b} <- RoseTree.new(:b),
...> {:ok, d} <- RoseTree.new(:d),
...> {:ok, z} <- RoseTree.new(:z),
...> {:ok, c} <- RoseTree.new(:c, [d, z]) do
...> RoseTree.new(:a, [b, c])
...> end
...> tree
...> |> Zipper.from_tree()
...> |> Zipper.descend(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: []}
]}
]}
Ascend from any node to the root of the tree.
Examples
iex> {:ok, tree} = with {:ok, b} <- RoseTree.new(:b), …> {:ok, d} <- RoseTree.new(:d), …> {:ok, z} <- RoseTree.new(:z), …> {:ok, c} <- RoseTree.new(:c, [d, z]) do …> RoseTree.new(:a, [b, c]) …> end …> tree …> |> Zipper.from_tree() …> |> Zipper.descend(1) …> |> Zipper.lift(&Zipper.descend(&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: []}
]}
]}
Extract the currently focused tree from a zipper.
Examples
iex> {:ok, tree} = with {:ok, b} <- RoseTree.new(:b),
...> {:ok, d} <- RoseTree.new(:d),
...> {:ok, z} <- RoseTree.new(:z),
...> {:ok, c} <- RoseTree.new(:c, [d, z]) do
...> RoseTree.new(:a, [b, c])
...> end
...> Zipper.descend({tree, []}, 0)
...> |> Zipper.lift(&Zipper.to_tree(&1))
%RoseTree{node: :b, children: []}