patternModerate
How could this be written using continuation passing style?
Viewed 0 times
thiswrittencontinuationpassingcouldstyleusinghow
Problem
Is there a way to use CPS in this code to make the two insert functions tail-call optimized?
As always any comments on idiomatic Elixir code and style are welcome as well.
defmodule BTree do
defstruct tree: {node: nil, left: nil, right: nil}
def new(element), do: %BTree{tree: {node: element, left: nil, right: nil}}
#insert when there's some data in the tree
def insert(%BTree{tree: {node: e, left: l, right: r}}, element) when e > element do
newl = if l == nil, do: new(element), else: insert(l, element)
%BTree{tree: {node: e, left: newl, right: r }}
end
def insert(%BTree{tree: {node: e, left: l, right: r}}, element) when e BTree.insert(5) |> BTree.insert(15) |> BTree.height()As always any comments on idiomatic Elixir code and style are welcome as well.
Solution
I would like to slightly rewrite the problem before making it continuation based because it will make it clearer and avoid duplication:
The previous code was generating a BTree for every node. Instead, we simply treat each node as a tuple with three elements (the node item, left and right). When inserting an element, we traverse the tree until we reach nil. We can make it tail recursive by passing a function and invoking the previous one at each step:
Note though I find this code much harder to understand and I don't think you would get any benefit in practice.
defmodule BTree do
defstruct tree: nil
def new(e), do: %BTree{tree: {e, nil, nil}}
def insert(%BTree{tree: root}, element) do
%BTree{tree: do_insert(root, element)}
end
defp do_insert(nil, element) do
{element, nil, nil}
end
defp do_insert({e, l, r}, element) when e > element do
{e, do_insert(l, element), r}
end
defp do_insert({e, l, r}, element) when e BTree.insert(5) |> BTree.insert(15) |> IO.inspectThe previous code was generating a BTree for every node. Instead, we simply treat each node as a tuple with three elements (the node item, left and right). When inserting an element, we traverse the tree until we reach nil. We can make it tail recursive by passing a function and invoking the previous one at each step:
defmodule BTree do
defstruct tree: nil
def new(e), do: %BTree{tree: {e, nil, nil}}
def insert(%BTree{tree: root}, element) do
do_insert(root, element, &%BTree{tree: &1})
end
defp do_insert(nil, element, fun) do
fun.({element, nil, nil})
end
defp do_insert({e, l, r}, element, fun) when e > element do
do_insert(l, element, &fun.({e, &1, r}))
end
defp do_insert({e, l, r}, element, fun) when e BTree.insert(5) |> BTree.insert(15) |> IO.inspectNote though I find this code much harder to understand and I don't think you would get any benefit in practice.
Code Snippets
defmodule BTree do
defstruct tree: nil
def new(e), do: %BTree{tree: {e, nil, nil}}
def insert(%BTree{tree: root}, element) do
%BTree{tree: do_insert(root, element)}
end
defp do_insert(nil, element) do
{element, nil, nil}
end
defp do_insert({e, l, r}, element) when e > element do
{e, do_insert(l, element), r}
end
defp do_insert({e, l, r}, element) when e < element do
{e, l, do_insert(r, element)}
end
defp do_insert({_, _, _} = tuple, _element) do
tuple
end
end
BTree.new(10) |> BTree.insert(5) |> BTree.insert(15) |> IO.inspectdefmodule BTree do
defstruct tree: nil
def new(e), do: %BTree{tree: {e, nil, nil}}
def insert(%BTree{tree: root}, element) do
do_insert(root, element, &%BTree{tree: &1})
end
defp do_insert(nil, element, fun) do
fun.({element, nil, nil})
end
defp do_insert({e, l, r}, element, fun) when e > element do
do_insert(l, element, &fun.({e, &1, r}))
end
defp do_insert({e, l, r}, element, fun) when e < element do
do_insert(r, element, &fun.({e, l, &1}))
end
defp do_insert({_, _, _} = tuple, _element, fun) do
fun.(tuple)
end
end
BTree.new(10) |> BTree.insert(5) |> BTree.insert(15) |> IO.inspectContext
StackExchange Code Review Q#51541, answer score: 11
Revisions (0)
No revisions yet.