所以我对Scala中Seq的这种行为感到很困惑。
使用模式匹配时,我可以使用::
或+:
运算符,它们似乎可以互换
val s=Seq(1,2,3)
s match{
case x :: l => ...
但是当我尝试在不同情况下使用::
时:
val s=1::Seq(2,3)
我收到"value :: is not a member of Seq[Int]"
条消息。我知道我应该使用+=
和=+
运算符与Seq,但为什么
::
仅适用于模式匹配方案吗?
答案 0 :(得分:8)
::
适用于List
,实际上Seq.apply
目前会为您提供List
:
scala> val s = Seq(1,2,3)
s: Seq[Int] = List(1, 2, 3)
因此值s
的类型为Seq[Int]
,但它指向的对象的类型为List[Int]
。这很好,因为List
扩展了Seq
。这当然会与涉及::
的模式匹配,因为它实际上是List
:
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)
您必须使用Seq
的方法:
scala> val s = 1 +: Seq(2,3)
s: Seq[Int] = List(1, 2, 3)
您混淆的关键是当您对s
之类的值调用方法时,可用的方法集完全取决于值 static 类型,而模式匹配检查匹配的对象是否为::
类。
为了显示这一点,让我们编译一些示例代码并使用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
最后,您可以问为什么Scala人没有为::
制作Seq
等效方法(前置元素)。如果有,那么1 :: Seq(2,3)
就可以了。
但对于Seq
,他们确实需要一个对的运算符,一个要预先添加(这个必须以冒号结束,以便它是右关联的)和一个要追加的运算符。您希望避免将元素附加到List
,因为您必须遍历现有元素才能执行此操作,但对于Seq
一般情况并非如此 - 例如追加对Vector
非常有效。因此,他们选择+:
作为前置,:+
作为追加。
当然,您可以问为什么他们没有使用+:
List
来匹配Seq
。我不知道答案的全部答案。我知道::
来自其他具有列表结构的语言,因此部分答案可能与已建立的约定一致。也许他们没有意识到他们想要一对匹配的{@ 1}}超类型算子,直到为时已晚 - 不确定。有谁知道这里的历史?
答案 1 :(得分:2)