HiveBrain v1.2.0
Get Started
← Back to all entries
patternModerate

How could this be written using continuation passing style?

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
thiswrittencontinuationpassingcouldstyleusinghow

Problem

Is there a way to use CPS in this code to make the two insert functions tail-call optimized?

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:

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.inspect


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:

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.inspect


Note 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.inspect
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 < 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.inspect

Context

StackExchange Code Review Q#51541, answer score: 11

Revisions (0)

No revisions yet.