为什么我可以在模式匹配中使用 :: 运算符和 Seq 而不是其他地方 [英] Why can I use :: operator with Seq in pattern matching but not elsewhere
问题描述
所以我对 Scala 中关于 Seq 的这种行为感到非常困惑.
So I have been really confused with this behavior regarding Seq in Scala.
使用模式匹配时,我可以使用 ::
或 +:
运算符,它们似乎可以互换
When using pattern matching I am able to use either ::
or +:
operator and they seem interchangeable
val s=Seq(1,2,3)
s match{
case x :: l => ...
但是当我尝试在不同的情况下使用 ::
时:
but when I'm trying to use ::
in different situation like so:
val s=1::Seq(2,3)
我收到 "value :: is not a member of Seq[Int]"
消息.我知道我应该在 Seq 中使用 +=
和 =+
运算符,但是为什么::
只适用于模式匹配场景吗?
I receive "value :: is not a member of Seq[Int]"
message. I understand that I am supposed to use +=
and =+
operators with Seq, but why
::
work only in pattern matching scenario?
推荐答案
::
用于List
s,实际上是Seq.apply
目前会给你一个List
:
::
is for List
s, and in fact Seq.apply
will currently give you a List
:
scala> val s = Seq(1,2,3)
s: Seq[Int] = List(1, 2, 3)
所以值s
的类型是Seq[Int]
,但它指向的对象是List[Int]
类型.这很好,因为 List
扩展了 Seq
.这当然会匹配一个包含 ::
的模式,因为它实际上是一个 List
:
So the type of value s
is Seq[Int]
, but the object it points to is of type List[Int]
. That's fine, because List
extends Seq
. And that will of course match a pattern involving ::
because it is in fact a List
:
scala> s match { case x :: xs => x }
res2: Int = 1
但是表达式Seq(1,2,3)
的类型不是List[Int]
而是Seq[Int]
--即使实际对象确实是一个 List
.所以以下失败,因为 Seq
没有定义 ::
方法:
But the type of expression Seq(1,2,3)
is not List[Int]
but Seq[Int]
-- even though the actual object is indeed a List
. So the following fails because Seq
does not define a ::
method:
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
的方法:
You have to use the method for Seq
instead:
scala> val s = 1 +: Seq(2,3)
s: Seq[Int] = List(1, 2, 3)
混淆的关键在于,当您对 s
之类的值调用方法时,可用的方法集完全取决于该值的 static 类型,而模式匹配检查被匹配的对象是否属于 ::
类.
The key to your confusion is that when you invoke a method on a value like s
, the set of methods available depends entirely on the value's static type, whereas the pattern match checks that the object being matched is of class ::
.
为了显示这一点,让我们编译一些示例代码并使用 javap
查看字节码;first
方法的前几条指令检查参数是否属于 ::
类(而不是其他扩展 Seq
的类)并转换为它:
To show this, let's compile some sample code and use javap
to see the bytecode; the first few instructions of the first
method check that the argument is of class ::
(rather than some other class extending Seq
) and cast to it:
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)
会起作用.
Finally, you could ask why the Scala folks didn't make ::
the equivalent method (prepend an element) for Seq
. If they had, then 1 :: Seq(2,3)
would work.
但是对于 Seq
,他们确实需要一对 运算符,一个用于前置(这个必须以冒号结尾,以便它是右结合的)和一个追加.您希望避免将元素附加到 List
,因为您必须遍历现有元素才能这样做,但通常情况下 Seq
并非如此——例如append 对于 Vector
非常有效.所以他们选择 +:
作为前置和 :+
作为追加.
But for Seq
they really needed a pair of operators, one to prepend (this one must end in a colon, so that it is right-associative) and one to append. You want to avoid appending an element to a List
because you have to traverse the existing elements to do so, but the same is not true for Seq
s in general -- e.g. append is quite efficient for a Vector
. So they chose +:
for prepend and :+
for append.
当然,你可能会问他们为什么不使用 +:
作为 List
来匹配 Seq
.我不知道完整的答案.我确实知道 ::
来自其他具有列表结构的语言,因此部分答案可能是与既定约定的一致性.也许他们没有意识到他们需要一对匹配的运算符来用于 List
的超类型,直到为时已晚——不确定.有人知道这里的历史吗?
Of course, you could ask why they didn't use +:
for List
to match Seq
. I don't know the full answer to that. I do know that ::
comes from other languages which have list structures, so in part the answer is probably consistency with established conventions. And perhaps they did not realize that they wanted a matching pair of operators for a supertype of List
until it was too late -- not sure. Does anyone know the history here?
这篇关于为什么我可以在模式匹配中使用 :: 运算符和 Seq 而不是其他地方的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!