【问题标题】:How to pattern match head and tail types of a scala list?如何对scala列表的头尾类型进行模式匹配?
【发布时间】:2016-03-10 23:05:37
【问题描述】:

我想pattern matchscalalist 的不同段上headtail 的类型:

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.tail :: tail) ^

我错过了什么?我怎么可能对列表中的headtail类型 进行模式匹配?

【问题讨论】:

  • 尝试将case (head: List[Int]) :: (tail: List[Any]) => 替换为case ( (headhead : Int) :: (headtail : List[Any]) ) :: (tail : List[Any]) =>,这样你就可以对抗类型擦除
  • 问题在于case (head: List[Any]) :: (tail: List[Any]) 行,您提到了不同的行,这也应该有助于该行吗???
  • 第三个选项与第二个选项冲突。所以你可以调整第二个选择或第三个选择。由于第三个选择更笼统,因此更容易使第二个更具体
  • 您是否声称我不需要第三种情况:case (head: List[Any]) :: (tail: List[Any]),因为它有冲突?
  • 我想您的某些功能需要所有选择。您也不应该丢弃。问题在于 scala 的类型擦除,无法区分第二个和第三个选择。因此,您应该更多地扩展第二选择以使类型与众不同。对于匹配所有类型的 scala 模式 List[blabla] 是相同的。但是您可以获取 List 的负责人并为其指定类型。并且该类型将被正确检查。在第二个选择中,head 的类型是 List[Int],它没有描述。所以你应该将head 进一步扩展为headhead :: headtail。现在 scala 会捕获类型

标签: scala pattern-matching


【解决方案1】:

你受到了对象系统的限制:

List[Int]Int 的唯一共同父级是 Any

因此,推理系统只能安全地假设您可以返回Any。 @jwvh 提出的解决方案是可行的,但可能存在运行时异常的危险。

如果您想以类型安全的方式解决问题,可以选择使用无形库 https://github.com/milessabin/shapelessHList : https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#heterogenous-lists

【讨论】:

  • 您能否在下面的答案中查看我的示例?我不认为 (2) 是正确的 - 序列知道内部元素的类型。它不只知道任务不需要的T。我同意HList 很棒,它们是解决此问题的可能选择之一,但这不是唯一的选择
  • 对于您的扩展答案,这是完全正确的。类型擦除不会影响您。
【解决方案2】:

我同意带有 HList 的 @Andreas 解决方案是解决问题的一个很好的例子,但我仍然不明白这有什么问题:

 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)

我在您的任务中没有发现类型擦除有任何问题,因为您实际上不需要测试已擦除的类型。在我的示例中,我有意跳过了所有已擦除的类型 - 以显示这一点。类型擦除不会清除列表元素的类型信息,它只清除列表泛型的类型信息,在我的例子中是 Any_ - 所以你根本不需要。如果我没有遗漏某些东西,那么在您的示例中,类型根本不会被删除,因为无论如何您几乎到处都有Any

【讨论】:

  • 你的观察是对的 :) 我应该调整答案吗?
  • 也许,我只是删除关于类型擦除的声明。我认为使用HList 的建议是一个不错的选择,但我不认为这是唯一的选择——没有它你也可以轻松生活。
  • 我可能应该提一下,我还没有使用 shapeless,所以我没有决定它是否是好方法的权威
  • 已修改并移除类型推理段落
猜你喜欢
  • 2013-04-10
  • 1970-01-01
  • 1970-01-01
  • 2020-12-28
  • 2021-02-09
  • 1970-01-01
  • 2014-09-26
  • 2011-10-12
  • 1970-01-01
相关资源
最近更新 更多