ðŸŽ‰ Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io ðŸŽ‰

# mendellev's solution

## to Zipper in the Scala Track

Published at Mar 13 2020 · 0 comments
Instructions
Test suite
Solution

Creating a zipper for a binary tree.

Zippers are a purely functional way of navigating within a data structure and manipulating it. They essentially contain a data structure and a pointer into that data structure (called the focus).

For example given a rose tree (where each node contains a value and a list of child nodes) a zipper might support these operations:

• `from_tree` (get a zipper out of a rose tree, the focus is on the root node)
• `to_tree` (get the rose tree out of the zipper)
• `value` (get the value of the focus node)
• `prev` (move the focus to the previous child of the same parent, returns a new zipper)
• `next` (move the focus to the next child of the same parent, returns a new zipper)
• `up` (move the focus to the parent, returns a new zipper)
• `set_value` (set the value of the focus node, returns a new zipper)
• `insert_before` (insert a new subtree before the focus node, it becomes the `prev` of the focus node, returns a new zipper)
• `insert_after` (insert a new subtree after the focus node, it becomes the `next` of the focus node, returns a new zipper)
• `delete` (removes the focus node and all subtrees, focus moves to the `next` node if possible otherwise to the `prev` node if possible, otherwise to the parent node, returns a new zipper)

The Scala exercises assume an SBT project scheme. The exercise solution source should be placed within the exercise directory/src/main/scala. The exercise unit tests can be found within the exercise directory/src/test/scala.

To run the tests simply run the command `sbt test` in the exercise directory.

Please see the learning and installation pages if you need any help.

## Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

### ZipperTest.scala

``````import org.scalatest.{FunSuite, Matchers}

/** @version created manually **/
class ZipperTest extends FunSuite with Matchers {
def empty[A]: Option[BinTree[A]] = None

def bt[A](v: A, l: Option[BinTree[A]], r: Option[BinTree[A]]): Option[BinTree[A]] =
Some(BinTree(v, l, r))

def leaf[A](v: A): Option[BinTree[A]] =
Some(BinTree(v, None, None))

val t1: BinTree[Int] = BinTree(1, bt(2, empty,   leaf(3)), leaf(4))
val t2: BinTree[Int] = BinTree(1, bt(5, empty,   leaf(3)), leaf(4))
val t3: BinTree[Int] = BinTree(1, bt(2, leaf(5), leaf(3)), leaf(4))
val t4: BinTree[Int] = BinTree(1, leaf(2),                 leaf(4))

def fromSome[T](o: Option[T]) = o.get

val z = Zipper

test("data is retained") {
z.toTree(z.fromTree(t1)) should be (t1)
}

test("left, right and value") {
pending
z.value(fromSome(z.right(fromSome(z.left(z.fromTree(t1)))))) should be (3)
}

pending
(z.left(fromSome(z.left(z.fromTree(t1))))) should be (None)
}

test("tree from deep focus") {
pending
z.toTree(fromSome(z.right(fromSome(z.left(z.fromTree(t1)))))) should be (t1)
}

test("setValue") {
pending
z.toTree(z.setValue(5, (fromSome(z.left(z.fromTree(t1)))))) should be (t2)
}

test("setLeft with Some") {
pending
z.toTree(z.setLeft(Some(BinTree(5, None, None)),
(fromSome(z.left(z.fromTree(t1)))))) should be (t3)
}

test("setRight with None") {
pending
z.toTree(z.setRight(None, (fromSome(z.left(z.fromTree(t1)))))) should be (t4)
}

test("different paths to same zipper") {
pending
z.right(fromSome(z.up(fromSome(z.left(z.fromTree(t1)))))) should be
(z.right(z.fromTree(t1)))
}
}``````
``````import scala.annotation.tailrec

object Zipper {
// A zipper for a binary tree.
//implement this http://blog.ezyang.com/2010/04/you-could-have-invented-zippers/

// Get a zipper focussed on the root node.
def fromTree[A](bt: BinTree[A]): Zipper[A] = Zipper(bt)

// Get the complete tree from a zipper.
@tailrec
def toTree[A](zipper: Zipper[A]): BinTree[A] = zipper match{
case Zipper(bt, TopContext()) => bt
case zip => toTree(up(zip).get)
}

// Get the value of the focus node.
def value[A](zipper: Zipper[A]): A = zipper.binTree.value

// Get the left child of the focus node, if any.
def left[A](zipper: Zipper[A]): Option[Zipper[A]] = {
if(zipper.binTree.left.isEmpty){
None
}else{
val lBt = zipper.binTree.left.get
Some(Zipper(lBt,RightContext(BinTree(zipper.binTree.value,right = zipper.binTree.right), zipper.context)))
}
}

// Get the right child of the focus node, if any.
def right[A](zipper: Zipper[A]): Option[Zipper[A]] = {
if(zipper.binTree.right.isEmpty){
None
}else{
val rBt = zipper.binTree.right.get
Some(Zipper(rBt,LeftContext(BinTree(zipper.binTree.value,left= zipper.binTree.left),zipper.context)))
}
}

// Get the parent of the focus node, if any.
def up[A](zipper: Zipper[A]): Option[Zipper[A]] = zipper.context match {
case TopContext() => None
case LeftContext(binTree, context)  =>  Some[Zipper[A]](Zipper[A](BinTree[A](binTree.value,left  =binTree.left, right  = Some(zipper.binTree)), context))
case RightContext(binTree, context) =>  Some(Zipper[A](BinTree[A](binTree.value,right =binTree.right, left   = Some(zipper.binTree)), context))
}

// Set the value of the focus node.
def setValue[A](v: A, zipper: Zipper[A]): Zipper[A] = Zipper(BinTree(v,zipper.binTree.left,zipper.binTree.right),zipper.context)

// Replace a left child tree.
def setLeft[A](l: Option[BinTree[A]], zipper: Zipper[A]): Zipper[A] = Zipper(BinTree(zipper.binTree.value,l,zipper.binTree.right),zipper.context)

// Replace a right child tree.
def setRight[A](r: Option[BinTree[A]], zipper: Zipper[A]): Zipper[A] = Zipper(BinTree(zipper.binTree.value,zipper.binTree.left,r),zipper.context)

}

case class Zipper[A](binTree: BinTree[A], context: Context[A] = TopContext[A]()){

}

// A binary tree.
case class BinTree[A](value: A, left: Option[BinTree[A]] = None, right: Option[BinTree[A]] = None)

sealed trait Context[A]

case class TopContext[A]() extends Context[A]

case class LeftContext[A](binTree: BinTree[A],context: Context[A]) extends Context[A]{

}

case class RightContext[A](binTree: BinTree[A],context: Context[A]) extends Context[A]{

}``````