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

Link to this type breadcrumb()
breadcrumb() :: %{parent: any, left_siblings: [any], right_siblings: [any]}
Link to this type either_zipper()
either_zipper() :: {:ok, RoseTree.Zipper.t} | {:error, tuple}

Link to this section Functions

Link to this function ascend(zipper)
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: []}
    ]}
  ]}, []}
}
Link to this function find_child(zipper, predicate)
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}}
Link to this function first?(zipper)
first?(RoseTree.Zipper.t) :: boolean

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
Link to this function first_child(zipper)
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: []}
  ]}
]}, []}
Link to this function has_children?(zipper)
has_children?(RoseTree.Zipper.t) :: boolean

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
Link to this function has_parent?(zipper)
has_parent?(RoseTree.Zipper.t) :: boolean

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
Link to this function insert_first_child(zipper, tree)
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: []}
      ]}
    ]
  }
]}}
Link to this function insert_last_child(zipper, tree)
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: []}
  ]
}
Link to this function insert_left(zipper, tree)

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: []}
      ]}
    ]
  }
]}}
Link to this function insert_nth_child(zipper, index, tree)
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}}
Link to this function insert_right(zipper, tree)

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: []}
      ]}
    ]
  }
]}}
Link to this function last?(zipper)
last?(RoseTree.Zipper.t) :: boolean

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
Link to this function last_child(zipper)
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: []}]
}}
Link to this function leaf?(zipper)
leaf?(RoseTree.Zipper.t) :: boolean

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
Link to this function lift(either_zipper_error, f)
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}}
Link to this function modify(zipper, f)
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: []}
    ]}
  ]
}]}
Link to this function next_sibling(zipper)
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}}
Link to this function nth_child(zipper, index)
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: []}
    ]}
  ]
}]}}
Link to this function only_child?(zipper)
only_child?(RoseTree.Zipper.t) :: boolean

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
Link to this function previous_sibling(zipper)
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: []}
  ]}
]}
Link to this function root?(zipper)
root?(RoseTree.Zipper.t) :: boolean

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: []},
  ]},
]}, []}

Extract the currently focused tree from a zipper.

Examples

iex> RoseTree.new(:a, [:b, RoseTree.new(:c, [:d, :z])])
...> |> Zipper.from_tree()
...> |> Zipper.nth_child(0)
...> |> Zipper.lift(&Zipper.to_tree(&1))
%RoseTree{node: :b, children: []}