Как сопоставить типы head и tail в списке scala?

Я хочу pattern match на разных сегментах a list на scala о типах head и tail:

class Solution07 extends FlatSpec with ShouldMatchers {
  "plain recursive flatten" should "flatten a list" in {
    val list1 = List(List(1, 1), 2, List(3, List(5, 8)))
    val list1Flattened = List(1, 1, 2, 3, 5, 8)

    flattenRecur(list1) should be (list1Flattened)
  }

  def flattenRecur(ls: List[Any]): List[Int] = ls match {
    case (head: Int) :: (tail: List[Any]) => head :: flattenRecur(tail)
    case (head: List[Int]) :: (tail: List[Any]) => head.head :: flattenRecur(head.tail :: tail)
    case (head: List[Any]) :: (tail: List[Any]) => flattenRecur(head) :: flattenRecur(tail) // non-variable type... on this line.
  }
}

Я:

ошибка: (18, 17) аргумент типа без переменных Int в шаблоне типа List[Int] (основа List[Int]) не отмечена, так как она исключены путем подчистки case (head: List[Int]):: (tail: List[Any]) => head.head :: flattenRecur (head.хвост: хвост) ^

что я не хватает? как это возможно для меня, чтобы шаблон совпадения на head и tail ' s типы из списка?

3 ответов


Я согласен, что решение @Andreas с HList является хорошим примером для решения проблемы, но я все еще не понимаю, в чем проблема с этим:

 def flatten(ls: List[_]): List[Int] = ls match {
    case Nil => Nil
    case (a: Int) :: tail => a :: flatten(tail)
    case (a: List[_]) :: tail => flatten(a) ::: flatten(tail)
    case _ :: tail => flatten(tail)
  }

затем:

println(flatten(List(List("one",9,8),3,"str",4,List(true,77,3.2)))) // List(9, 8, 3, 4, 77)

Я не вижу никаких проблем с стиранием типов в вашей задаче, потому что вам на самом деле не нужно тестировать типы, которые стираются. Я намеренно пропустил все удаленные типы в моем примере - чтобы показать это. Тип erasure не очищает информацию о типе элементов списка, это очищает только информацию о типе списка generic, который у вас есть Any или _ в моем случае - так что вам не нужно. Если я не упускаю что-то, в вашем примере типы вообще не стираются, потому что у вас все равно есть Any почти везде.


вас поражают ограничения, заданные объектной системой:

единственный общий родитель для List[Int] и Int is Any

поэтому из-за этого система вывода может только безопасно предположить, что вы можете вернуть Any. Предлагаемое решение @jwvh выполнимо, но имеет возможную опасность исключений во время выполнения.

если вы хотите решить проблему в typesafe способом вариант может быть использовать HList бесформенной библиотеки https://github.com/milessabin/shapeless : https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#heterogenous-lists


из-за стирания типа вы можете прибегнуть к проверке каждого элемента.

def flattenRecur(ls: List[Any]): List[Int] = ls match {
  case head :: tail if (head.isInstanceOf[Int])
                     => head.asInstanceOf[Int] :: flattenRecur(tail)
  case head :: tail if (head.isInstanceOf[List[Any]])
                     => flattenRecur(head.asInstanceOf[List[Any]]) ++
                        flattenRecur(tail)
  case _ :: tail => flattenRecur(tail)
  case _ => Nil
}

который, похоже, работает:

scala> flattenRecur(List(List("one",9,8),3,"str",4,List(true,77,3.2)))
res8: List[Int] = List(9, 8, 3, 4, 77)

но, как общее правило, избежать Any обычно стоит затраченных усилий.