2
votes

I new to Scala. As an exercise I am trying to write a match statement over a list of tuples with guards. I am aware that a map would solve the problem but I am trying to gain understanding into pattern matching.

I seek to write a function that takes a List[(Char, Int)] as argument. The function sorts the entries and if two entries have the same key value they are added together. So the following argument List(('q', 1'), ('a', 1), ('c', 2), ('a', 2), ('c', 1)) would become List(('a', 3), ('c', 3'), ('q', 1)).

I come with the following code:

def sortAndAggregateList(chars: List[(Char, Int)]) : List[(Char, Int)] = {
  chars match {
    case (charP1, numP1) :: (charP2, numP2) :: (x : List[(String, Int)]) if (charP1 > charP2) =>
      sortAndAggregateList((charP2, numP2) :: (charP1, numP1) :: x)
    case (charP1, numP1) :: (charP2, numP2) :: (x : List[(String, Int)]) if (charP1 < charP2) =>
      sortAndAggregateList((charP1, numP1) :: (charP2, numP2) :: x)
    case (charP1, numP1) :: (charP2, numP2) :: (x : List[(String, Int)]) if (charP1 == charP2) =>
      sortAndAggregateList((charP1, numP1 + numP2) :: x)
    case Nil =>
      Nil
  }
}

But I get the following warning:

:14: warning: fruitless type test: a value of type List[(Char, Int)] cannot also be a List[(String, Int)] (the underlying of List[(String, Int)]) (but still might match its erasure)

I tried dropping the List but if I do that I get an error that x is of type Any.

Any suggestions?

2
See the error message: you are matching against chars which is a List[(Char,Int)] but in the pattern you expect x to be a List[(String, Int)].Josef

2 Answers

2
votes

the error is your type check which you do after each case statement (: List[(String, Int)]).

If you change your code to the following the error disappears:

def sortAndAggregateList(chars: List[(Char, Int)]) : List[(Char, Int)] = {
  chars match {
    case (charP1, numP1) :: (charP2, numP2) :: x if (charP1 > charP2) =>
      sortList(p1 :: p2 :: x)
    case (charP1, numP1) :: (charP2, numP2) :: x if (charP1 < charP2) =>
      sortList(p2 :: p1 :: x)
    case (charP1, numP1) :: (charP2, numP2) :: x if (charP1 == charP2) =>
        val p3: (Char, Int) = (charP1, numP1 + numP2)
        sortList(p3 :: x)
    case x =>
      x
    case Nil =>
      Nil
  }
}

Afterwards you'll find out that the compiler tells you that p1 and p2 are undefined. To fix this you need to set them as p1 = (charP1, numP1) and p2 = (charP2, numP2). To solve this with your syntax you can do the following:

def sortAndAggregateList(chars: List[(Char, Int)]) : List[(Char, Int)] = {
  chars match {
    case (charP1, numP1) :: (charP2, numP2) :: x if (charP1 > charP2) =>
      sortList((charP1, numP1) :: (charP2, numP2) :: x)
    case (charP1, numP1) :: (charP2, numP2) :: x if (charP1 < charP2) =>
      sortList((charP2, numP2) :: (charP1, numP1) :: x)
    case (charP1, numP1) :: (charP2, numP2) :: x if (charP1 == charP2) =>
        val p3: (Char, Int) = (charP1, numP1 + numP2)
        sortList(p3 :: x)
    case x =>
      x
    case Nil =>
      Nil
  }
}

Now the only missing link is the sortList function which you've not added. I'm not sure if this will work because I think the case:

case x => x

should be:

case x :: Nil => x :: Nil

Otherwise x would match anything. Which also leaves you the possibility to remove the case:

case Nil => Nil

if you don't want to remove case x => x

0
votes

Extra type annotation after x is not necessary and wrong.

remove this

(x : List[(String, Int)])

instead use (not compulsory. you can omit the type annotation)

(x : List[(Char, Int)])

Complete function

  def sortAndAggregateList(chars: List[(Char, Int)]): List[(Char, Int)] = chars match {
    case (charP1, numP1) :: (charP2, numP2) :: x if charP1 > charP2 =>

      sortAndAggregateList((charP2, numP2) :: (charP1, numP1) :: x)

    case (charP1, numP1) :: (charP2, numP2) :: x if charP1 < charP2 =>

      sortAndAggregateList((charP1, numP1) :: (charP2, numP2) :: x)

    case (charP1, numP1) :: (charP2, numP2) :: x if charP1 == charP2 =>

      sortAndAggregateList((charP1, numP1 + numP2) :: x)

    case x => x
  }

The code will be much more cleaner if you consider collapsing the tuples

  def sortAndAggregateList(chars: List[(Char, Int)]): List[(Char, Int)] = chars match {
    case a :: b :: x if a._1 > b._2 =>

      sortAndAggregateList(b :: a :: x)

    case a :: b :: x if a._1 < b._1 =>

      sortAndAggregateList(a :: b :: x)

    case a :: b :: x if a._1 == b._1 =>

      sortAndAggregateList((a._1, (a._2 + b._2)) :: x)

   case x => x

  }

case case x => x will match both list Nil case and list having one element case.