View Source Hold.Zipper.Tree (hold v0.1.0)

Zipper over a General Tree (any number of child nodes).

Intro on Zippers: https://ferd.ca/yet-another-article-on-zippers.html

Summary

Types

t()

A thread is a list of znodes behind us in the traversal. They support being able to walk backwards the way we came.

Znodes represent levels of the tree as a Zipper List.

Functions

Goes down one level to the children of current focus.

Same as children/1 but will throw if not able to make move

Returns whether current focus has any children

Returns tree rooted at element based on predicate function - returning nil when no element returns true for predicate.

Returns the currently focused element of the tree.

Adds a new node at the current focus with value.

Adds a new child node to the current focus with value.

Adds a new node to the right of current focus with value.

Moves focus to the left of the current level

Same as left/1 but will throw if not able to make move

Returns whether a left siblings exists

Creates an iterator over a tree walking over all of the elements until completion.

Moves up to direct parent of current focus leaving current siblings intact.

Same as parent/1 but will throw if not able to make move

Moves focus to the right of the current level

Same as right/1 but will throw if not able to make move

Returns whether a right sibling exists.

Creates a new tree with value placed as root.

Returns whether current focus is the root node

Moves up to parent similar to parent/1, but also resets current level

Same as rparent/1 but will throw if not able to make move

Updates the value currently at focus with value.

Types

@type t() :: {thread(), znode()}
@type t(a) :: {thread(a), znode(a)}
@type thread() :: [znode()]

A thread is a list of znodes behind us in the traversal. They support being able to walk backwards the way we came.

@type thread(a) :: [znode(a)]
@type znode() ::
  Hold.Zipper.List.t({term(), Hold.Zipper.List.t(znode())})
  | Hold.Zipper.List.t({term(), nil})

Znodes represent levels of the tree as a Zipper List.

Given this tree:

    A
      
 B     C

If the focus was at B the resulting znode would be.

{ # left siblings [], # current focus | right siblings [

{
  B,
  nil
},
{
  C,
  nil
},

] }

The focus of the zipper is a 2 element tuple. The first item represents the currently focused node. the second element represents the focused nodes children. nil shows that it has no children and a znode when it has them.

@type znode(a) ::
  Hold.Zipper.List.t({a, Hold.Zipper.List.t(znode(a))})
  | Hold.Zipper.List.t({a, nil})

Functions

@spec children(t()) :: {:ok, t()} | Hold.Zipper.invalid_move()

Goes down one level to the children of current focus.

Exmaples

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> {:ok, tree} = Hold.Zipper.Tree.children(tree)
iex> Hold.Zipper.Tree.focus(tree)
1
@spec children!(t()) :: t()

Same as children/1 but will throw if not able to make move

@spec children?(t()) :: boolean()

Returns whether current focus has any children

Examples

iex> tree = Hold.Zipper.Tree.root(0)
iex> Hold.Zipper.Tree.children?(tree)
false

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> Hold.Zipper.Tree.children?(tree)
true
Link to this function

find_subtree(tree, pred)

View Source
@spec find_subtree(t(), (t(), focus :: any() -> boolean())) :: t() | nil

Returns tree rooted at element based on predicate function - returning nil when no element returns true for predicate.

The predicate function will be invoked with the tree at the position of the current element and the current element. The tree argument can be used if there is a need to look at surrounding elements of the current focus in order for the predicate to make a decision.

@spec focus(t()) :: term()

Returns the currently focused element of the tree.

Examples

iex> tree = Hold.Zipper.Tree.root(0)
iex> Hold.Zipper.Tree.focus(tree)
0

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> Hold.Zipper.Tree.focus(tree)
1 
@spec insert(t(), term()) :: t()

Adds a new node at the current focus with value.

Examples

Inserted value becomes focus

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> tree = Hold.Zipper.Tree.insert(tree, 2)
iex> Hold.Zipper.Tree.focus(tree)
2

Old focus is now the right sibling

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> tree = Hold.Zipper.Tree.insert(tree, 2)
iex> tree = Hold.Zipper.Tree.right!(tree)
iex> Hold.Zipper.Tree.focus(tree)
1
Link to this function

insert_child(arg, value)

View Source
@spec insert_child(t(), term()) :: t()

Adds a new child node to the current focus with value.

If the parent were to then move into the child collection they would be focused on the new child.

Examples

Focus stays at parent.

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> Hold.Zipper.Tree.focus(tree)
0

Child is inserted.

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> Hold.Zipper.Tree.focus(tree)
1

Child goes to the head of the right sibling list.

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 2)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> tree = Hold.Zipper.Tree.right!(tree) # Now 2 is in left siblings
iex> tree = Hold.Zipper.Tree.parent!(tree)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> tree = Hold.Zipper.Tree.left!(tree)
iex> Hold.Zipper.Tree.focus(tree)
2
Link to this function

insert_right(arg, value)

View Source
@spec insert_right(t(), term()) :: t()

Adds a new node to the right of current focus with value.

Does not shift focus to new node.

Examples

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> tree = Hold.Zipper.Tree.insert_right(tree, 2)
iex> Hold.Zipper.Tree.focus(tree)
1

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> tree = Hold.Zipper.Tree.insert_right(tree, 2)
iex> tree = Hold.Zipper.Tree.right!(tree)
iex> Hold.Zipper.Tree.focus(tree)
2

Examples

@spec left(t()) :: {:ok, t()} | Hold.Zipper.invalid_move()

Moves focus to the left of the current level

Examples

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 2)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> tree = Hold.Zipper.Tree.right!(tree)
iex> {:ok, tree} = Hold.Zipper.Tree.left(tree)
iex> Hold.Zipper.Tree.focus(tree)
1
@spec left!(t()) :: t()

Same as left/1 but will throw if not able to make move

@spec left?(t()) :: boolean()

Returns whether a left siblings exists

Examples

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> Hold.Zipper.Tree.left?(tree)
false

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 2)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> tree = Hold.Zipper.Tree.right!(tree)
iex> Hold.Zipper.Tree.left?(tree)
true
@spec next(t()) :: {:ok, any(), t()} | {:complete, any(), t()}

Creates an iterator over a tree walking over all of the elements until completion.

Each iteration will return {:ok, item, new_tree} until the tree has been fully visited. At which point {:complete, last_item, new_tree} will be returned

@spec parent(t()) :: {:ok, t()} | Hold.Zipper.invalid_move()

Moves up to direct parent of current focus leaving current siblings intact.

The current level siblings list will not be reset before moving up. For example if you were on the second sibling of the current level moving up with parent/1 and then calling children/1 would move you back down to the second sibling again.

Examples

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> {:ok, tree} = Hold.Zipper.Tree.parent(tree)
iex> Hold.Zipper.Tree.focus(tree)
0

Moving to parent doesn't reset left and right sibling list.

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 2)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> tree = Hold.Zipper.Tree.right!(tree) # 1 is now in left sibling list
iex> {:ok, tree} = Hold.Zipper.Tree.parent(tree)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> Hold.Zipper.Tree.focus(tree)
2
@spec parent!(t()) :: t()

Same as parent/1 but will throw if not able to make move

@spec right(t()) :: {:ok, t()} | Hold.Zipper.invalid_move()

Moves focus to the right of the current level

Examples

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 2)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> {:ok, tree} = Hold.Zipper.Tree.right(tree)
iex> Hold.Zipper.Tree.focus(tree)
2
@spec right!(t()) :: t()

Same as right/1 but will throw if not able to make move

@spec right?(t()) :: boolean()

Returns whether a right sibling exists.

Examples

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> Hold.Zipper.Tree.right?(tree)
false

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 2)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> Hold.Zipper.Tree.right?(tree)
true
@spec root(term()) :: t()

Creates a new tree with value placed as root.

Examples

iex> tree = Hold.Zipper.Tree.root(0)
iex> Hold.Zipper.Tree.focus(tree)
0
@spec root?(t()) :: boolean()

Returns whether current focus is the root node

Examples

iex> tree = Hold.Zipper.Tree.root(0)
iex> Hold.Zipper.Tree.root?(tree)
true

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> Hold.Zipper.Tree.root?(tree)
false
@spec rparent(t()) :: {:ok, t()} | Hold.Zipper.invalid_move()

Moves up to parent similar to parent/1, but also resets current level

If for example you were on the second sibling of the current level moivng up with rparent/1 and then calling children/1 would move you back down to first sibling instead of the second.

Examples

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> {:ok, tree} = Hold.Zipper.Tree.parent(tree)
iex> Hold.Zipper.Tree.focus(tree)
0

Unlike parent/1 the left and right sibling list is reset

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 2)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> tree = Hold.Zipper.Tree.right!(tree) # 1 is now in left sibling list
iex> {:ok, tree} = Hold.Zipper.Tree.rparent(tree) # This resets sibling list
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> Hold.Zipper.Tree.focus(tree)
1
@spec rparent!(t()) :: t()

Same as rparent/1 but will throw if not able to make move

@spec update(t(), term()) :: t()

Updates the value currently at focus with value.

The new value at node will keep the children present in the old node.

Examples

Update the current focus.

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.update(tree, 0.5)
iex> Hold.Zipper.Tree.focus(tree)
0.5

Children of the updated node do not change.

iex> tree = Hold.Zipper.Tree.root(0)
iex> tree = Hold.Zipper.Tree.insert_child(tree, 1)
iex> tree = Hold.Zipper.Tree.update(tree, 0.5)
iex> tree = Hold.Zipper.Tree.children!(tree)
iex> Hold.Zipper.Tree.focus(tree)
1