Scala 为什么我可以在模式匹配中使用带Seq的::运算符,但不能在其他地方使用
因此,我对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
:
或+:
运算符,并且它们看起来可以互换
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;我会改正的。