Scala 为什么我可以在模式匹配中使用带Seq的::运算符,但不能在其他地方使用

Scala 为什么我可以在模式匹配中使用带Seq的::运算符,但不能在其他地方使用,scala,Scala,因此,我对Scala中有关Seq的行为感到非常困惑 当使用模式匹配时,我可以使用:或+:运算符,并且它们看起来可以互换 val s=Seq(1,2,3) s match{ case x :: l => ... 但是当我尝试在不同的情况下使用:时,例如: val s=1::Seq(2,3) 我收到“value::不是Seq[Int]的成员”消息。我知道我应该对Seq使用+=和=+运算符,但为什么 :仅在模式匹配场景中工作?:用于列表s,实际上Seq.apply当前将为您提供一个列表: s

因此,我对Scala中有关Seq的行为感到非常困惑

当使用模式匹配时,我可以使用
+:
运算符,并且它们看起来可以互换

val s=Seq(1,2,3)
s match{
case x :: l => ...
但是当我尝试在不同的情况下使用
时,例如:

val s=1::Seq(2,3)
我收到
“value::不是Seq[Int]的成员”
消息。我知道我应该对Seq使用
+=
=+
运算符,但为什么
仅在模式匹配场景中工作?

用于
列表
s,实际上
Seq.apply
当前将为您提供一个
列表

scala> val s = Seq(1,2,3)
s: Seq[Int] = List(1, 2, 3)
scala> s match { case x :: xs => x }
res2: Int = 1
因此,值
s
的类型是
Seq[Int]
,但它指向的对象的类型是
List[Int]
。这很好,因为
List
扩展了
Seq
。这当然会匹配一个涉及
的模式,因为它实际上是一个
列表

scala> val s = Seq(1,2,3)
s: Seq[Int] = List(1, 2, 3)
scala> s match { case x :: xs => x }
res2: Int = 1
但是表达式
Seq(1,2,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)
造成混淆的关键是,当您对值(如
s
)调用方法时,可用的方法集完全取决于该值的静态类型,而模式匹配检查要匹配的对象是否属于类

为了说明这一点,让我们编译一些示例代码,并使用
javap
查看字节码;
first
方法的前几条指令检查参数是否属于类
(而不是扩展
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
NS%cat Test.scala
对象测试{
def first(xs:Seq[Int])=xs match{case x::xs=>x}
}
NS%javap-c测试\$.class
从“Test.scala”编译而来
公开期末考试${
公共静态最终测试$MODULE$;
公共静态{};
代码:
0:新的#2//类测试$
3:invokespecial#12//方法“”:()V
6:返回
公共int优先(scala.collection.Seq);
代码:
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//方法scala/collection/immutable/$colon$colon.head:()Ljava/lang/Object;
18:invokestatic#26//方法scala/runtime/BoxesRunTime.unextpoint:(Ljava/lang/Object;)I
21:istore 4
23:iload 4
25:istore 5
27:iload 5
29:我轮到你了
30:new#28//class scala/MatchError
33:dup
34:aload_2
35:invokespecial#31//方法scala/MatchError。“:(Ljava/lang/Object;)V
38:athrow
最后,您可能会问,为什么Scala人员没有为
Seq
创建等价的方法(预先添加元素)。如果他们有,那么
1::Seq(2,3)
就可以了

但是对于
Seq
来说,他们确实需要一对运算符,一个用于前置(这一个必须以冒号结尾,这样它才是右关联的)还有一个要追加。您希望避免将元素追加到
列表中,因为这样做必须遍历现有的元素,但对于
Seq
通常情况下情况并非如此——例如,追加对于
向量
非常有效。因此,他们选择
+:
作为前置,选择
:+
作为追加


当然,你可以问他们为什么不使用
+:
来匹配
列表
。我不知道完整的答案。我知道
来自其他具有列表结构的语言,所以部分答案可能是与既定惯例的一致性。也许他们没有意识到他们想为
列表的超类型找到一对匹配的运算符,但为时已晚--不确定。有人知道这里的历史吗?

::
是的运算符。 是所有Scala序列继承的通用特性。这是任何类型序列的通用接口,而不仅仅是列表

假定默认情况下Scala使用
list
作为
Seq()
工厂方法返回的序列,那么模式匹配可以使用cons操作

所以你可以

val s = 1::List(2,3)
但不是

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

谢谢你指出我的不精确的语言,m-z;我会改正的。