I'm new to Scala and the only way I saw people access case classes was with pattern matching.
I recently solved a Huffman coding problem with the following code
abstract class TreeNodes
case class Leaf(weight: Int, value: String) extends TreeNodes
case class Node(weights: Int, left: TreeNodes, right: TreeNodes) extends TreeNodes
// P50
def huffman(freq: List[(String,Int)]): List[(String,String)] = {
def makeNode(left: TreeNodes, right: TreeNodes): Node =
(left,right) match
case (Leaf(w1,_),Leaf(w2,_)) => Node(w1 w2, left,right)
case (Node(w1,_,_),Leaf(w2,_)) => Node(w1 w2, left ,right)
case (Leaf(w1,_), Node(w2,_,_)) => Node(w1 w2, left, right)
case (Node(w1,_,_), Node(w2,_,_)) => Node(w1 w2, left, right)
def makeLeaves(freq: List[(String,Int)]): List[TreeNodes] = freq.map((s: String, i: Int) => Leaf(i,s))
def makeTree(nodes: List[TreeNodes]): List[TreeNodes] = {
if nodes.size == 1 then nodes else {
val sortedNodes = nodes.sortBy(_ match {case Leaf(w,_) => w; case Node(w,_,_) => w})
makeTree(sortedNodes.appended(makeNode(sortedNodes.head,sortedNodes.tail.head)).drop(2))
}
}
def traverseTree(node: TreeNodes, acc: String): List[(String, String)] = {
node match
case Leaf(w, s) => List((s,acc))
case Node(w, left, right) => traverseTree(left, acc "0") ::: traverseTree(right, acc "1")
}
traverseTree(makeTree(makeLeaves(freq)).head, "").sortBy(_._1)
}
where the input and output should be like so:
scala> huffman(List(("a", 45), ("b", 13), ("c", 12), ("d", 16), ("e", 9), ("f", 5)))
res0: List[String, String] = List((a,0), (b,101), (c,100), (d,111), (e,1101), (f,1100))
The code is correct and produces the desired output. However, I want to refactor the makeNode
function to a cleaner version, since all cases have the same return.
I want a more concise version. Is there a way to create a common field for different case classes or any other way to access case classes without pattern matching?
CodePudding user response:
Just declare the common methods in the common interface, in this case, add def weight: Int
or val weight: Int
to the base class.
In Scala 2.x:
sealed abstract class TreeNodes {
def weight: Int
}
case class Leaf(weight: Int, value: String) extends TreeNodes
case class Node(weight: Int, left: TreeNodes, right: TreeNodes) extends TreeNodes
//...
def makeNode(left: TreeNodes, right: TreeNodes): Node =
Node(left.weight right.weight, left,right)
In Scala 3.x:
enum TreeNodes:
case Leaf(weight: Int, value: String)
case Node(weight: Int, left: TreeNodes, right: TreeNodes)
def weight: Int
but remember to import the Leaf
and Node
if you want that the rest of the code works as-is: import TreeNodes._
.