1

I'm learning Scala by working the exercises from the book "Scala for the Impatient". One of the questions asks:

/**
   * Q5: One can use lists to model trees that store values only in the leaves. For example, the list ((3, 8) 2 (5))
   * describes the tree
   *      .
   *    . 2   .
   *  3   8  5
   *
   *  However, some of the list elements are numbers and others are lists. In Scala, you cannot have heterogeneous lists,
   *  so you have to use a `List[Any]`. Write a `leafSum` function to compute the sum of all elements in the leaves,
   *  using pattern matching to differentiate between numbers and lists.
   *
   */

My code:

def leafSum(lst: List[Any]): Int = {
  type Node = Tuple2[_, List[Any]]
  type Leaf = Tuple2[Int, Int]

  lst.foldLeft(0) {
    case (_, elem): Node => leafSum(elem)
    case (_, _): Leaf => _ + _
  }
}

However, both case statements fail to compile with the following error. Why?

type mismatch; found : Unit required: Int

Edit: I know I can use collect instead but I'd like foldLeft to do the sum and not having to do it myself.

lst.collect {
  case node: List[Any] => leafSum2(node)
  case leaf: Int => leaf
}.sum

Edit2: See my solution below.

Abhijit Sarkar
  • 21,927
  • 20
  • 110
  • 219

2 Answers2

2

The pattern matching needed some tweaks. Let me know if this works.

  // No need for the type aliases to be redefined on each function call
  type Node = Tuple2[_, List[Any]]
  type Leaf = Tuple2[Int, Int]

  def leafSum(lst: List[Any]): Int = {
     lst.foldLeft(0) {
       case (sum, x: List[_]) => sum + leafSum(x)
       case (a: Int, b: Int) => a + b
     }
  }
marios
  • 8,874
  • 3
  • 38
  • 62
  • 1
    You're not using the types in your solution. I wanted to make it clear what the `case` statements are dealing with by defining logical types. – Abhijit Sarkar Jun 07 '15 at 00:46
  • 1
    I know. This is in general due to JVM's type erasure. That is, any parametrized type gets stripped of the types at runtime (type are only there for type checking during compilation). This is why you can't have something like this `case x: List[Int] => ...` since the type of the list (Int in this case) gets erased. – marios Jun 07 '15 at 00:51
  • I know and don't care about `List[Int]`; I should be able to check for `List`. What I mean is that the types defined (`Node` and `Leaf`) are not used, I'm not talking about type erasure. – Abhijit Sarkar Jun 07 '15 at 00:53
  • Look, if the types get erased, it means that your Node and Leaf are essentially indistinguishable. They are both `Tuple2[_,_]`. Type erasure does that, that's why I did't use the types in the pattern matching. – marios Jun 07 '15 at 00:55
  • 1
    That makes sense but your code doesn't work as expected. Fails the test case for the tree in the question. `"Method leafSum" should "compute the sum of all elements in the leaves" in {` `leafSum(List(List(3, 8), 2, List(5))) should be(18)` `}` – Abhijit Sarkar Jun 07 '15 at 01:06
1

This works. @marios answer led me to the right path, even though his implementation had a bug and didn't work.

def leafSum(lst: List[Any]): Int = {
  lst.foldLeft(0) {
    case (sum, node: List[_]) => sum + leafSum(node)
    case (sum, leaf: Int) => sum + leaf
  }
}
Abhijit Sarkar
  • 21,927
  • 20
  • 110
  • 219