Collapse similar case statements in Scala - scala

Is there an elegant way to do something like the following example using just one case statement?
foobar match {
case Node(Leaf(key, value), parent, qux) => {
// Do something with parent & qux
}
case Node(parent, Leaf(key, value), qux) => {
// Do something with parent & qux (code is the same as in the previous case)
}
// other cases
}
For an understanding of what is going on here: foobar is a node of a binary tree, and I match the cases when one of the node's ancestors is a Leaf node. These are the classes used:
abstract class Tree
case class Node(left: Tree, right: Tree, critBit: Int) extends Tree
case class Leaf(key: String, value:String) extends Tree

You can use a custom extractor to abstract the matching part away from the logic part:
object Leafed {
def unapply(tree: Tree) = tree match {
case Node(Leaf(_, _), parent, qux) => Some((parent, qux))
case Node(parent, Leaf(_, _), qux) => Some((parent, qux))
case _ => None
}
}
And then you can define methods like this:
def doSomething(tree: Tree): Int = tree match {
case Leafed(parent, qux) => qux
case _ => -100
}
Which you can use like this:
scala> val leaf = Leaf("foo", "bar")
leaf: Leaf = Leaf(foo,bar)
scala> doSomething(leaf)
res7: Int = -100
scala> doSomething(Node(leaf, Node(leaf, leaf, 5), 10))
res8: Int = 10
scala> doSomething(Node(Node(leaf, leaf, 5), leaf, 10))
res9: Int = 10
Otherwise you're out of luckā€”as Marth notes above, pattern alternatives aren't going to help you here.

Related

Deserialize a binary tree breadth-first in Functional Programming

I had an imperative program which deserializes to a Binary Tree from an array. Its a BFS algorithm. I was wondering how to do this in Scala with functional programming concepts.
class TreeNode(_value: Int = 0, _left: TreeNode = null, _right: TreeNode = null) {
val value: Int = _value
val left: TreeNode = _left
val right: TreeNode = _right
}
def createTree(list: Array[Int]): TreeNode = ???
For reference this is the imperative version in Javascript. The algorithm is described here. https://support.leetcode.com/hc/en-us/articles/360011883654-What-does-1-null-2-3-mean-in-binary-tree-representation-
class TreeNode {
constructor(val){
this.val = val;
this.left = this.right = null;
}
}
function makeTree(arr){
let root = new TreeNode(arr[0]);
let q = [root];
let index = 1;
while(q.length){
let node = q.splice(0,1)[0];
if(arr[index] != null){
node.left = new TreeNode(arr[index]);
q.push(node.left);
}
index++;
if(arr[index] != null){
node.right = new TreeNode(arr[index]);
q.push(node.right);
}
index++;
}
return root;
}
First of all, you can use a case class to simplify your tree class, and you should use Option instead of null:
case class Tree(value: Int, left: Option[Tree], right: Option[Tree])
Next, the main trouble here is because your tree is immutable, it needs to be built with a depth-first post-order traversal, and your serialization format requires a breadth-first level-order traversal. So you first have to deserialize to a data structure that can then be traversed in depth-first order. The following uses a Map from (row, column) to the node value:
#scala.annotation.tailrec
private def bfsTraverse(
serialized: List[Option[Int]],
queue: Queue[(Int, Int)],
map: Map[(Int, Int), Int]): Map[(Int, Int), Int] = {
val ((row, col), queueTail) = queue.dequeue
if (serialized.isEmpty) {
map
} else if (serialized.head.isEmpty) {
bfsTraverse(serialized.tail, queueTail, map)
} else {
val newQueue = queueTail.enqueueAll(List((row + 1, col * 2), (row + 1, col * 2 + 1)))
val newMap = map + ((row, col) -> serialized.head.get)
bfsTraverse(serialized.tail, newQueue, newMap)
}
}
Now you can use the output of that function to build your Tree:
private def buildTree(row: Int, col: Int, map: Map[(Int, Int), Int]): Option[Tree] = {
map.get((row, col)).map{value =>
Tree(value, buildTree(row + 1, col * 2, map), buildTree(row + 1, col * 2 + 1, map))
}
}
This solution is a bit verbose but uses some functional concepts and defines the data structures thoroughly.
The algorithm you provided works better with mutable nodes. It's possible to have a shorter solution with just one mutable class, but here, there are two versions (one node class with mutable left/right and the other completely immutable).
Case classes automatically provide a lot of useful features such as comparison and friendly print-out
The processValues function tail-recursively performs the tasks equivalent to the makeTree function you provided.
The #tailrec annotation tells the compiler to check that the function is tail recursive.
Pattern matching using the match and case keywords replace checking for null-ness or different subtypes, and the compiler can check for a non-exhaustive match clause.
The App trait allows you to easily make an object (static class) into an entrypoint to run some examples.
import scala.annotation.tailrec
sealed trait TreeNode[T]
sealed trait MutableTreeNode[T]
object MutableTreeNode {
final case class Empty[T]() extends MutableTreeNode[T]
case class Branch[T](val value: T) extends MutableTreeNode[T] {
protected var left: MutableTreeNode[T] = Empty()
protected var right: MutableTreeNode[T] = Empty()
def setLeft(newLeft: T): Branch[T] = {
left = Branch(newLeft)
left.asInstanceOf[Branch[T]] // shouldn't be necessary but compiler requires it
}
def setRight(newRight: T): Branch[T] = {
right = Branch(newRight)
right.asInstanceOf[Branch[T]]
}
override def toString: String = this.toImmutable().toString
/* converts given node to immutable version */
private def toImmutable(node: MutableTreeNode[T]): TreeNode[T] = {
node match {
case Empty() => TreeNode.Empty()
case b#Branch(value) => TreeNode.Branch(value, toImmutable(b.left), toImmutable(b.right))
}
}
def toImmutable():TreeNode[T] = toImmutable(this)
}
/**
* Modifies nodes inside of queue
*/
#tailrec def processValues[T](values: Seq[Option[T]], queue: Seq[MutableTreeNode.Branch[T]]): Unit = {
(queue, values) match {
case (Nil, _) => ()
case (_, Nil) => ()
case (qHead :: qTail, Some(vLeft) :: Some(vRight) :: vTail) =>
processValues(vTail, qTail :+ qHead.setLeft(vLeft) :+ qHead.setRight(vRight))
case (qHead :: qTail, Some(vLeft) :: None :: vTail) =>
processValues(vTail, qTail :+ qHead.setLeft(vLeft))
case (qHead :: qTail, None :: Some(vRight) :: vTail) =>
processValues(vTail, qTail :+ qHead.setRight(vRight))
case (qHead :: qTail, None :: None :: vTail) =>
processValues(vTail, qTail)
}
}
}
object TreeNode {
final case class Empty[T]() extends TreeNode[T]
final case class Branch[T](value: T, left: TreeNode[T], right: TreeNode[T]) extends TreeNode[T]
def deserialize[T](values: Seq[Option[T]]): TreeNode[T] = {
values match {
case Some(headVal) :: tail =>
val root: MutableTreeNode.Branch[T] = MutableTreeNode.Branch(headVal)
MutableTreeNode.processValues(tail, Seq(root))
root.toImmutable()
case Nil => Empty()
case _ => throw new RuntimeException("Invalid argument values")
}
}
}
object TreeNodeTest extends App {
val input = Seq(Some(5), Some(4), Some(7), None, None, Some(2), None)
val treeNode:TreeNode[Int] = TreeNode.deserialize(input)
println(treeNode)
}
As has been noted, Scala avoids null whenever possible, preferring Option to indicate the absence of a value.
Mutable variables are also shunned, which makes it much easier to construct a B-tree in a depth-first manner rather than breadth-first.
So all you really need is an easy-to-use breadth-first-serialization --to--> depth-first-serialization translator.
I did it in two steps.
//from Breadth-First-Serialization to full tree representation
def BFS2full[A](bfs:IndexedSeq[Option[A]]) :List[List[Option[A]]] = {
val bfsLen = bfs.length
if (bfs.isEmpty) Nil
else
List(bfs.head) :: List.unfold((List(bfs.head),1)){case (pr,idx) =>
Option.when(bfsLen > idx){
val ns = pr.foldLeft((List.empty[Option[A]],idx)){
case ((acc,x), None) => (acc ++ List(None,None), x)
case ((acc,x), _) => (acc ++ List(bfs.lift(x).flatten
,bfs.lift(x+1).flatten), x+2)
}
(ns._1, ns)
}
}
}
//from full tree representation to Depth-First-Serialization
def toDFS[A](lloa :List[List[Option[A]]]
,lvl :Int = 0) :List[Option[A]] = lloa match {
case Nil => List(None, None)
case List(None) :: Nil => List(None)
case List( oa ) :: tl => oa :: toDFS(tl, lvl)
case row :: tl => row.drop(lvl*2) match {
case List(None,None,_*) => List(None, None)
case List(None, ob ,_*) => None :: (ob::toDFS(tl,2*lvl + 1))
case List( oa ,None,_*) => (oa::toDFS(tl,2*lvl)) ++ List(None)
case List( oa , ob ,_*) => (oa :: toDFS(tl, 2*lvl)) ++
(ob :: toDFS(tl,2*lvl + 1))
}
}
Now let's parameterize the tree so that we can build Int trees, Float trees, String trees, etc.
We're also going to make the constructor private so that node creation is only done via factory methods.
case class Tree[A] private (value : A
,left : Option[Tree[A]]
,right : Option[Tree[A]])
All that's left is to supply the factory methods.
object Tree {
private def BFS2full[A]( . . . //as above
private def toDFS[A]( . . . //as above
def fromDFS[A](dfs :IterableOnce[Option[A]]) :Option[Tree[A]] = {
val itr = dfs.iterator
def loop(): Option[Tree[A]] =
Option.when(itr.hasNext)(itr.next())
.flatten
.map(new Tree(_,loop(),loop()))
loop()
}
def fromBFS[A](bfs:IndexedSeq[Option[A]]) :Option[Tree[A]] =
fromDFS(toDFS(BFS2full(bfs)))
}
testing:
Tree.fromBFS(Vector(Some('A'),None,Some('B'),Some('C'))).get
//res0: Tree[Char] = Tree(A,None,Some(Tree(B,Some(Tree(C,None,None)),None)))
Here's a basic solution. It doesn't use lazy vals or any data structures other than List, Option, and Either. You might find it easier to understand because of that or harder because of the verbosity.
I've defined Tree like this, just to make things easier.
sealed trait Tree[+T]
case class Node[+T](data: T, left: Tree[T], right: Tree[T]) extends Tree[T]
case object Empty extends Tree[Nothing]
Also, instead of an Array[Int], I'm using a List[Option[T]] (where T is a type parameter of the makeTree method). Some means there's a node, None is like -1 in your code. This is more idiomatic and also works for types other than Int.
The thing about doing this breadth-first is that you need to kinda keep passing the input list around to the children. First you try to make the left child, then when that's done, you try make the right child. Then you try to make the left child's children, then the right child's children, then their children, and so on.
One way to deal this without using var would be to take in the input list containing the serialized binary tree and see what the head of the list is. If it's a None, we can just return an Empty, because we know it's the end of the tree. If it's a Some however, we can't yet return a tree, but we can return another function that takes in the next round of input and returns a tree.
Since there are 2 different types that can be returned, these functions will be of type List[Option[T]] => Either[List[Option[T]] => ThisFunctionsType, Tree[T]]. Since the function may return another function of the same type, we'll have to define a new type that can return itself:
trait RecFun[T] extends ((List[Option[T]]) => (List[Option[T]], Either[RecFun[T], Tree[T]]))
The reason that it's List[Option[T]] => (List[Option[T]], Either[RecFun[T], Tree[T]]) and not just List[Option[T]] => Either[RecFun[T], Tree[T]] is that in case one child turns out to be a leaf somewhere in the middle of the list, we still need to continue, so the first element of the returned tuple contains the rest of the list after processing.
Now we can define this. The helper function is so that as long as the RecFun returns a Left[RecFun], it keeps passing the remaining input into that function.
def makeTree[T](list: List[Option[T]]): Tree[T] = {
def helper(f: RecFun[T], l: List[Option[T]]): Tree[T] =
f(l) match {
case (_, Right(tree)) => tree
case (next, Left(f)) => helper(f, next)
}
list match {
case Some(x) :: tail => helper(createRec(x), tail)
case _ => Empty
}
}
def createRec[T](data: T): RecFun[T] = {
case None :: Nil | Nil => (Nil, Right(Node(data, Empty, Empty)))
case Some(l) :: Nil => (Nil, Right(Node(data, Node(l, Empty, Empty), Empty)))
case lo :: ro :: rest =>
(rest, (lo, ro) match {
case (Some(l), Some(r)) =>
Left(waitForChildren(data, createRec(l), createRec(r)))
case (Some(l), None) =>
Left(waitForChild(Node(data, _, Empty), createRec(l)))
case (None, Some(r)) =>
Left(waitForChild(Node(data, Empty, _), createRec(r)))
case (None, None) => Right(Node(data, Empty, Empty))
})
}
def waitForChildren[T](data: T, leftF: RecFun[T], rightF: RecFun[T]): RecFun[T] =
input => {
val (next, res) = leftF(input)
res match {
case Right(tree) =>
(next, Left(waitForChild(Node(data, tree, _), rightF)))
case Left(leftF2) => {
val (next2, res2) = rightF(next)
(next2, Left(res2 match {
case Right(tree) => waitForChild(Node(data, _, tree), leftF2)
case Left(rightF2) => waitForChildren(data, leftF2, rightF2)
}))
}
}
}
def waitForChild[T](ctor: Tree[T] => Node[T], f: RecFun[T]): RecFun[T] =
input => {
val (next, res) = f(input)
(next, res match {
case Right(tree) => Right(ctor(tree))
case Left(recFun) => Left(waitForChild(ctor, recFun))
})
}
Scastie

Scala order implicit conversions?

I have some problem in scala order implicit, can someone help me?
Below are some classes definition, and what I want to do is to compare Leaf and Node through it's 'popularity'.
class Tree
case class EmptyTree() extends Tree
case class Leaf(label: String, popularity: Double) extends Tree
case class Node(popularity: Double, left: Tree, right: Tree) extends Tree
for exampel:
val a = Leaf("a",10)
val b = Leaf("b",20)
val c = Node(30,a,b)
if we want to compare a and b through it's popularity, it is easy to do by adding the implicit transformations, like that:
implicit val leavesOrder = new Ordering[Leaf] {
override def compare(x: Leaf, y: Leaf) =
implicitly[Ordering[Double]].compare(x.popularity, y.popularity)
}
but if I want to compare a and c through it's popularity, I am confused about it and don't konw how to add the implicit transformations?
Can someone help me?
You can create an implicit Ordering for Tree if you want to compare Leafs and Nodes.
Here's some (incomplete) code, on how you could do that:
implicit val treeOrder = new Ordering[Tree] {
override def compare(x: Tree, y: Tree) = (x,y) match {
case (Leaf(_,xP), Leaf(_,yP)) => xP compare yP
case (Node(xP,_,_), Leaf(_,yP)) => xP compare yP
case (Node(xP,_,_), Node(yP,_,_)) => xP compare yP
case (EmptyTree(), _) => -1
/* Add the rest of the cases here */
}
}
Bonus points for changing Tree to be a sealed trait, so that the compiler can tell you when your pattern matching is incomplete :)
Id do something like this. Change your Tree class to a sealed trait this means the pattern matching is exhaustive so the compiler can tell you if your missing something. Then you need to match on each of the types that a Tree can be. Not all of them have a popularity.
sealed trait Tree
case object EmptyTree extends Tree
case class Leaf(label: String, popularity: Double) extends Tree
case class Node(popularity: Double, left: Tree, right: Tree) extends Tree
implicit val treeOrdering = new Ordering[Tree] {
private val doubleOrdering = implicitly[Ordering[Double]]
def compare(a: Tree, b: Tree): Int = {
(a, b) match {
case (Node(p1, _, _), Node(p2, _, _)) => doubleOrdering.compare(p1, p2)
case (Leaf(_, p1), Node(p2, _, _)) => doubleOrdering.compare(p1, p2)
case (Node(p1, _, _), Leaf(_, p2)) => doubleOrdering.compare(p1, p2)
case (Leaf(_, p1), Leaf(_, p2)) => doubleOrdering.compare(p1, p2)
case (EmptyTree, _) => -1
case (_, EmptyTree) => 1
}
}
}

How to copy an object in Scala?

I have this tree structure
sealed trait Tree
case class Node(var left: Tree, var right: Tree, var value: String) extends Tree
case object EmptyNode extends Tree
and a Tree object called myTree. I want to make another tree with the exact same structure and values called otherTree, but myTree.clone() does not work. What else can I do?
Something like this will copy your Tree:
def copyTree(t:Tree):Tree = {
t match {
case EmptyNode => EmptyNode
case Node(left, right, value) => Node(copyTree(left), copyTree(right), value)
}
}
Just for completeness - as the question asks for 'How to copy...' and you mentioned clone(): You can copy case classes with the copy() method. Yet it won't help with a tree structure that is mutable. But with an immutable structure (as Gregor suggests above), e.g. like this
sealed trait Tree
case class Node(left: Tree, right: Tree, value: String) extends Tree
case object EmptyNode extends Tree { override def toString = " _ " }
and some data
scala> val leaf1 = Node(EmptyNode, EmptyNode, "leaf1")
scala> val leaf2 = Node(EmptyNode, EmptyNode, "leaf2")
scala> val tree1 = Node(leaf1, leaf2, "tree1")
scala> tree1
res0: Node = Node(Node( _ , _ ,leaf1),Node( _ , _ ,leaf2),tree1)
you can copy your tree like this
scala> val tree2 = tree1.copy(value = "tree2")
tree2: Node = Node(Node( _ , _ ,leaf1),Node( _ , _ ,leaf2),tree2)
or
scala> val tree3 = Node(tree1.left, tree1.right, "tree3")
tree3: Node = Node(Node( _ , _ ,leaf1),Node( _ , _ ,leaf2),tree3)
As usual with immutable instances, for updates you would need to create new tree structures for example using recursive operations as in Noah's answer.

scala count number of nodes in a tree

I have a tree defined as
sealed trait Tree[+A]
case class Leaf[A](value: A) extends Tree[A]
case class Branch[A](left: Tree[A], right: Tree[A]) extends Tree[A]
I would like a function to count the number of nodes in the tree and a function that counts the number of leafs
(I wrote a function to counts the number of leafs but I am not satisfied of the method that I am using. I would like a more "functional" implementation of that one).
val t1 = Branch(
Branch(
Leaf(12),
Branch(
Leaf(3),
Leaf(4))),
Leaf(8)) //> t1 : trees.Branch[Int] = Branch(Branch(Leaf(12),Branch(Leaf(3),Leaf(4))),Le
//| af(8))
def n_nodes(t:Tree[Int]):Int = {
var s = 0
def n_nodesAcc(t:Tree[Int]):Unit = {
t match {
case Branch(left, right) =>{
n_nodesAcc(left)
n_nodesAcc(right )
}
case Leaf(v) => {s = s+ 1}
}
}
n_nodesAcc(t)
s
} //> n_nodes: (t: trees.Tree[Int])Int
n_nodes(t1) //> res0: Int = 4
(this is an exercise)
You might write a recursive method to count the leaves:
def countLeaves[A](tree: Tree[A]): Int = tree match {
case l:Leaf[A] => 1
case b:Branch[A] => countLeaves(b.left) + countLeaves(b.right)
}
Or count all the nodes:
def countNodes[A](tree: Tree[A]): Int = tree match {
case l:Leaf[A] => 1
case b:Branch[A] => 1 + countLeaves(b.left) + countLeaves(b.right)
}
You could also write a similar method to just get all the leaves, and you could have more flexibility to do different functions later:
def getLeaves[A](tree: Tree[A]): Seq[Leaf[A]] = tree match {
case l:Leaf[A] => Seq(l)
case b:Branch[A] => getLeaves(b.left) ++ getLeaves(b.right)
}
And then count with getLeaves(t1).length. Or, similarly, get all the nodes:
def getNodes[A](tree: Tree[A]): Seq[Tree[A]] = tree match {
case l:Leaf[A] => Seq(l)
case b:Branch[A] => Seq(b) ++ getNodes(b.left) ++ getNodes(b.right)
}

How does "apply" work in an companion object (with Trait) in Scala?

Source: Functional Programming in Scala MEAP v10
In the below pasted code
sealed trait List[+A]
case object Nil extends List[Nothing]
case class Cons[+A](head: A, tail: List[A]) extends List[A]
object List {
def sum(ints: List[Int]): Int = ints match {
case Nil => 0
case Cons(x,xs) => x + sum(xs)
}
def product(ds: List[Double]): Double = ds match {
case Nil => 1.0
case Cons(0.0, _) => 0.0
case Cons(x,xs) => x * product(xs)
}
def apply[A](as: A*): List[A] = {
if (as.isEmpty) Nil
else Cons(as.head, apply(as.tail: _*))
}
val example = Cons(1, Cons(2, Cons(3, Nil)))
}
The Cons object is constructed presumably by the apply(), but the type signatures are different, how does scala end up assembling the Cons instance.
also while there is no un-apply, though the below code works absolutely fine, disassembling the List into Cons(head, tail)
object a{
val x = List(1,2,3,4,5) match {
case Cons(x, Cons(2, Cons(4, _))) => x
case Nil => 42
case Cons(x, Cons(y, Cons(3, Cons(4, _)))) => x + y
case Cons(h, t) => h + List.sum(t)
case _ => 101
}
}
A Cons instance is always constructed by Cons' constructor. You can call it directly:
val myList = new Cons(1, new Cons(2, Nil)) // list containing the elements 1 and 2
The apply method on the companion object is a factory method that allows you to construct a list with a nicer syntax :
val myList = List(1, 2) // expanded to List.apply(1, 2) by the compiler ; same result as above
There is no reason why they would need to have the same type signature, since they don't need to be called the same way.
As for why you can use pattern-matching without defining a unapply method : that's because you defined Nil and Cons as case classes/objects. A case class gets a number of functionalities for free (generated by the compiler), including the unapply method (but also equals, hashcode and toString for instance).
Edit: Some precisions based on the comments:
Constructors in Scala:
In Scala, each class has a default constructor, whose parameters are automatically available as fields in the class - so the default constructor doesn't need to have its own method body. For instance, the following Scala classes:
class Foo1(bar: Int)
class Foo2(val bar: Int)
Are roughly equivalent to the following Java classes:
public class Foo1 {
public Foo1(int bar) {
this.bar = bar;
}
private int bar;
}
public class Foo2 {
public Foo2(int bar) {
this.bar = bar;
}
private int bar;
public int getBar() {
return bar;
}
}
Polymorphism:
Nil and Cons both extend List. This means that a Cons is a kind of List. So if you create an instance of Cons with val myList = new Cons(1, Nil), myList is an object of type Cons... But it's also:
of type List
of type AnyRef, because it's the root class of all reference types. All classes in Scala extend AnyRef by default, so List extends AnyRef (because it doesn't have an explicit extends clause).
of type Any, which is the root of all Scala types.
The following code uses your List/Cons/Nil implementations:
val myList = List(1, 2, 3)
// Cons(1,Cons(2,Cons(3,Nil))) => result of the toString method generated by the compiler because it's a case class
myList.getClass.getName
// Cons => this is the concrete type of myList
myList.isInstanceOf[Cons[_]]
// true => myList is a Cons
myList.isInstanceOf[Nil.type]
// false
myList.isInstanceOf[List[_]]
// true => but it's also a kind of List
val foo: List[Int] = myList
// => this is allowed
myList.isInstanceOf[AnyRef]
// true
myList.isInstanceOf[Any]
// true
val anotherList = List()
// Nil
anotherList.getClass.getName
// Nil$ => the name is mangled with a $ sign to differentiate the 'object Nil' in case you also declare a 'class Nil'
anotherList.isInstanceOf[List[_]]
// true