加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 为什么我可以在模式匹配中使用:: operator和Seq,但在其

发布时间:2020-12-16 18:20:57 所属栏目:安全 来源:网络整理
导读:所以我对 Scala中Seq的这种行为感到困惑. 使用模式匹配时,我可以使用::或:运算符,它们似乎可以互换 val s=Seq(1,2,3)s match{case x :: l = ... 但是当我试图在不同的情况下使用:: val s=1::Seq(2,3) 我收到“value ::不是Seq [Int]”成员的消息.我明白我应
所以我对 Scala中Seq的这种行为感到困惑.

使用模式匹配时,我可以使用::或:运算符,它们似乎可以互换

val s=Seq(1,2,3)
s match{
case x :: l => ...

但是当我试图在不同的情况下使用::

val s=1::Seq(2,3)

我收到“value ::不是Seq [Int]”成员的消息.我明白我应该使用Seq的=和=运算符,但为什么呢
::仅在模式匹配场景中工作?

解决方法

::是列表,事实上Seq.apply目前会给你一个列表:

scala> val s = Seq(1,3)
s: Seq[Int] = List(1,3)

所以值s的类型是Seq [Int],但它指向的对象是List [Int]类型.那没关系,因为List扩展了Seq.那当然会匹配一个涉及::的模式,因为它实际上是一个List:

scala> s match { case x :: xs => x }
res2: Int = 1

但是表达式Seq(1,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的值调用方法时,可用的方法集完全取决于值的静态类型,而模式匹配则检查匹配的对象是否为class ::.

为了说明这一点,让我们编译一些示例代码并使用javap来查看字节码;第一个方法的前几个指令检查参数是否为class ::(而不是其他一些扩展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,因为您必须遍历现有元素才能执行此操作,但对于Seqs通常不会这样 – 例如追加对于Vector非常有效.所以他们选择:for prepend和:for append.

当然,你可以问为什么他们没有使用:for List匹配Seq.我不知道答案的完整答案.我知道::来自其他具有列表结构的语言,因此部分答案可能与已建立的约定一致.也许他们没有意识到他们想要一个匹配的运算符对于超类型的List,直到为时已晚 – 不确定.有谁知道这里的历史?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读