为什么我可以在模式匹配中使用 :: 运算符和 Seq 而不是其他地方 [英] Why can I use :: operator with Seq in pattern matching but not elsewhere

查看:50
本文介绍了为什么我可以在模式匹配中使用 :: 运算符和 Seq 而不是其他地方的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我对 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?

推荐答案

:: 用于Lists,实际上是Seq.apply 目前会给你一个List:

:: is for Lists, 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 Seqs 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屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆