【问题标题】:pattern match,"::" unapply confusion模式匹配,"::" 不适用混淆
【发布时间】:2013-07-23 17:00:26
【问题描述】:

我是 scala 的新手,在研究模式匹配部分时,我很困惑。

val hd::tail = List(1,2,3,4,5)

执行此代码后,hd 将为 Int = 1,tail 将为 List[Int] = List(2, 3, 4, 5)。 这段代码等于:

val ::(hd,tail) = List(1,2,3,4,5)

我了解到这段代码是模式匹配,它实际上调用了 unapply 方法。 但是当我这样写代码时,编译错误:

val result = ::.unapply(List(1,2,3,4,5))

它说方法参数类型不匹配。 "::" 的 unapply 方法需要一个 "::[?]"

有人知道原因吗?

【问题讨论】:

  • 这可能是因为(hd,tail) 不是一个有效的标识符。

标签: scala


【解决方案1】:

类型不匹配错误是因为::.unapply 采用::[T] 的实例,而不是更通用类型List[T] 的实例。 Scala 将自动添加与实际类型的匹配。 换句话说,我的理解是当你这样做时:

val list = List(1,2,3,4,5)
val hd::tail = list

编译器会生成类似这样的东西:

val list = List(1,2,3,4,5)
val (hd, tail) = 
  ::.unapply(
    // First match to test against input type
    // (necessary as the static type of `list` is `List[Int]`
    // so we don't know until runtime if it is an instance of `::[Int]`
    // or an instance of `Nil`)
    list match { 
      case nonEmptyList: ::[Int] => nonEmptyList 
      case _ => throw new MatchError 
    }
  ) match { // Second match to test against result of `::`.unapply
    case Some( result ) => result
    case _ => throw new MatchError 
  }

【讨论】:

  • +1 了解详情,ty。两场比赛能合二为一吗? case Some(nonEmptyResult : ::[Int]) => nonEmptyResult
  • 已接受。但我仍然想知道这个编译器生成适用于所有模式匹配还是仅适用于 List 情况?
  • 总是。一旦静态输入类型是unapply 的预期类型的​​子类型,这必然需要运行时类型检查。
  • @Richard Sitze:这主要是说明性的。实际上,您需要知道的只是运行时类型检查会发生。您可以反汇编生成的代码,并查看这是通过 instanceof/checkcast 一对字节码指令完成的(或者,如果您愿意,这只是 if (x.isInstanceOf[::]) ::.unapply(x.asInstanceOf[::]) else throw new MatchError。无论是直接发出,还是通过优化后期编译器通过目前超出我的知识范围,实际上是一个实现细节。
  • @RégisJean-Gilles:好吧,我又搞糊涂了。“只要静态输入类型是 unapply 的预期类型的​​子类型”,就不是 List 的父类型: : ?
【解决方案2】:

您的方法签名不正确。 :: 是一个案例类,因此 unapply 采用 :: 的实例。这里有一些更接近您可能正在寻找的东西,但通常我不会在代码中调用 unapply,因为它是在模式匹配场景中完成的:

  //Note the type returned by unapply
  val unappliedOption:Option[(Int, List[Int])] = ::.unapply(::(1, List(2, 3, 4, 5)))

  val (hd, tail) = unappliedOption.getOrElse((1, Nil))

在模式匹配中,unapply 会自动为您调用。还要注意:: 是一个非空列表,尽管它仍然返回option。所以对于List 的实例,我们有两个选项::Nil

  List(1, 2, 3, 4, 5) match {
    case hd :: tail => println(hd, tail) // calls ::.unapply and returns hd tail if the option is Some
    case hd => println(hd) //Default case Nil
  }

【讨论】:

  • 感谢您的回答!我想知道模式匹配的理论。书中说模式匹配实际上调用了 unapply 方法。所以我的问题中的 3 行代码应该是一样的,但是第三行编译失败,这让我很困惑。
  • ::.unapply 采用案例类::。在您的示例中,您给它一个List,它可能是空的,而:: 是非空的。在所有案例类中,unapply 都采用案例类的一个实例,因此您可以取回案例类中包含的内容。如果您查看:: 的定义,它会扩展List,就像Nil 扩展List
  • 那为什么第一行和第二行代码有效?他们不是也用 List 调用 unapply 方法吗?
  • 是的,如果您使用空列表 val hd::tail = List() 调用它,您将收到匹配错误。这就像我的第二个示例,没有处理 Nil 的默认情况。
  • 那么当代码用val hd::tail = List(1,2,3)这样的模式匹配样式编写时,编译器首先将List转换为::然后调用unapply方法?
猜你喜欢
  • 1970-01-01
  • 2011-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-14
  • 2019-10-02
相关资源
最近更新 更多