微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

scala – 为什么我可以在模式匹配中使用:: operator和Seq,但在其他地方不能

所以我对 Scala中Seq的这种行为感到困惑.

使用模式匹配时,我可以使用::或:运算符,它们似乎可以互换

val s=Seq(1,2,3)
s match{
case x :: l => ...

但是当我试图在不同的情况下使用::

val s=1::Seq(2,3)

我收到“value ::不是Seq [Int]”成员的消息.我明白我应该使用Seq的=和=运算符,但为什么呢
::仅在模式匹配场景中工作?

解决方法

::是列表,事实上Seq.apply目前会给你一个列表:

scala> val s = Seq(1,3)
s: Seq[Int] = List(1,3)

所以值s的类型是Seq [Int],但它指向的对象是List [Int]类型.那没关系,因为List扩展了Seq.那当然会匹配一个涉及::的模式,因为它实际上是一个List:

scala> s match { case x :: xs => x }
res2: Int = 1

但是表达式Seq(1,3)的类型不是List [Int]而是Seq [Int] – 即使实际对象确实是List.因此,以下失败是因为Seq没有定义::方法

scala> val s = 1 :: Seq(2,3)
<console>:7: error: value :: is not a member of Seq[Int]
       val s = 1 :: Seq(2,3)

您必须使用Seq的方法

scala> val s = 1 +: Seq(2,3)

您混淆的关键是当您对类似s的值调用方法时,可用的方法集完全取决于值的静态类型,而模式匹配则检查匹配的对象是否为class ::.

为了说明这一点,让我们编译一些示例代码并使用javap来查看字节码;第一个方法的前几个指令检查参数是否为class ::(而不是其他一些扩展Seq的类)并强制转换为它:

NS% cat Test.scala
object Test {
  def first(xs: Seq[Int]) = xs match { case x :: xs => x }
}

NS% javap -c Test\$.class
Compiled from "Test.scala"
public final class Test${
  public static final Test$MODULE$;

  public static {};
    Code:
       0: new           #2                  // class Test$
       3: invokespecial #12                 // Method "<init>":()V
       6: return

  public int first(scala.collection.Seq<java.lang.Object>);
    Code:
       0: aload_1
       1: astore_2
       2: aload_2
       3: instanceof    #16                 // class scala/collection/immutable/$colon$colon
       6: ifeq          30
       9: aload_2
      10: checkcast     #16                 // class scala/collection/immutable/$colon$colon
      13: astore_3
      14: aload_3
      15: invokevirtual #20                 // Method scala/collection/immutable/$colon$colon.head:()Ljava/lang/Object;
      18: invokestatic  #26                 // Method scala/runtime/BoxesRunTime.unBoxToInt:(Ljava/lang/Object;)I
      21: istore        4
      23: iload         4
      25: istore        5
      27: iload         5
      29: ireturn
      30: new           #28                 // class scala/MatchError
      33: dup
      34: aload_2
      35: invokespecial #31                 // Method scala/MatchError."<init>":(Ljava/lang/Object;)V
      38: athrow

最后,您可以问为什么Scala人没有为Seq制作::等效方法(前置元素).如果有,那么1 :: Seq(2,3)就可以了.

但是对于Seq来说,他们确实需要一对运算符,一个是前置的(这个必须以冒号结尾,因此它是右关联的)和一个要追加的运算符.您希望避免将元素附加到List,因为您必须遍历现有元素才能执行此操作,但对于Seqs通常不会这样 – 例如追加对于Vector非常有效.所以他们选择:for prepend和:for append.

当然,你可以问为什么他们没有使用:for List匹配Seq.我不知道答案的完整答案.我知道::来自其他具有列表结构的语言,因此部分答案可能与已建立的约定一致.也许他们没有意识到他们想要一个匹配的运算符对于超类型的List,直到为时已晚 – 不确定.有谁知道这里的历史?

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐