gpt4 book ai didi

scala - 为什么我可以在模式匹配中使用::运算符和 Seq 而不是其他地方

转载 作者:行者123 更新时间:2023-12-04 22:45:05 25 4
gpt4 key购买 nike

所以我对 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 中使用 +==+ 运算符,但是为什么 :: 仅适用于模式匹配场景吗?

最佳答案

:: 用于 List s,实际上 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 之类的值调用方法时,可用的方法集完全取决于该值的静态类型,而模式匹配会检查匹配的对象是否属于 :: 类。

为了说明这一点,让我们编译一些示例代码并使用 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 并非如此——例如append 对于 Vector 非常有效。所以他们选择 +: 作为前置, :+ 作为追加。

当然,你可以问他们为什么不使用 +: 来匹配 List 。我不知道完整的答案。我确实知道 Seq 来自具有列表结构的其他语言,所以部分答案可能是与既定约定的一致性。也许他们没有意识到他们需要一对匹配的运算符来用于 :: 的父类(super class)型,直到为时已晚——不确定。有人知道这里的历史吗?

关于scala - 为什么我可以在模式匹配中使用::运算符和 Seq 而不是其他地方,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30959597/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com