【问题标题】:Scala pattern matching for vararg可变参数的 Scala 模式匹配
【发布时间】:2017-10-29 17:56:44
【问题描述】:

我只是想动手实践一下 scala 并尝试自己实现 List.concat 函数。这是代码

  def concat[A](lists : Traversable[A]*):List[A]={
    println("concat called")
    lists match {
      case Nil => Nil
      case x :: Nil => (x :\ List.empty[A])((elem,list)=> elem::list)
      case x:: xs => (x :\ concat(xs:_*))((elem,list)=> elem :: list)
    }
  }

但是,当我尝试像

一样调用此方法时
concat(List(1,2,3),List(2,3,4),List(4,5,6),List(6,7,8))

我得到错误

Exception in thread "main" scala.MatchError: WrappedArray(List(1, 2, 3), List(2, 3, 4), List(4, 5, 6), List(6, 7, 8)) (of class scala.collection.mutable.WrappedArray$ofRef)

有人可以解释我在这里做错了什么吗? 提前致谢

【问题讨论】:

    标签: scala pattern-matching


    【解决方案1】:

    Varags 是 Seq,您可以像在 Seq 上一样匹配它,而不是像在列表上一样。这是一个例子:

    @ a(1, 2, 3) 
    res1: Seq[Int] = Array(1, 2, 3)
    @ def a(x: Int*) = x match {
                      case Seq() => "empty"
                      case Seq(a) => s"single $a"
                      case Seq(a, as @ _*) => s"multiple: $a, $as"
                    } 
    defined function a
    @ a(1, 2, 3, 4) 
    res3: String = "multiple: 1, WrappedArray(2, 3, 4)"
    @ a(1, 2) 
    res4: String = "multiple: 1, WrappedArray(2)"
    @ a(1) 
    res5: String = "single 1"
    

    Nilx :: xs 进行此类匹配通常意味着您可以简单地使用foldLeft,它就是这样做的。

    def concat[A](lists: Traversable[A]*): List[A] =
        lists.foldLeft(List.empty[A])(_ ++ _)
    

    请注意,匹配Nilx :: xs,其中xs 可以是Nil,就足够了。您的第二个case 可以简单地删除。

    看看那些:

    case Nil => Nil
    case x :: Nil => (x :\ List.empty[A])(_ :: _)
    case x :: xs  => (x :\ concat(xs:_*))(_ :: _)
    

    最后两个是一样的。如果在第三种情况下xs == Nil 则代替concat(xs:_*),您将获得Nil,这与List.empty[A] 相同(如果类型推断正确)。

    【讨论】:

      【解决方案2】:

      NilList

      scala> Nil
      res11: scala.collection.immutable.Nil.type = List()
      

      但是,Scala 将所有可变参数包装到 Seq 中(WrappedArray 实现 Seq),这就是您得到 MatchError 的原因。您可以通过以下方式重写您的函数:

      scala>   def concat[A](lists : Traversable[A]*):List[A]={
           |     lists match {
           |       case Seq() => Nil
           |       case x +: Seq() => (x :\ List.empty[A])((elem,list)=> elem::list)
           |       case x +: xs => (x :\ concat(xs:_*))((elem,list)=> elem :: list)
           |     }
           |   }
      concat: [A](lists: Traversable[A]*)List[A]
      
      scala> concat(List(1), List(2), List(3))
      res9: List[Int] = List(1, 2, 3)
      

      你也可以使用flatMap来简化你的函数:

      scala> def concat[A](lists: Traversable[A]*): List[A] = {
           |     lists.flatMap(x => x).toList
           | }
      concat: [A](lists: Traversable[A]*)List[A]
      
      scala> concat(List(1), List(2), List(3))
      res16: List[Int] = List(1, 2, 3)
      

      【讨论】:

      • 他也可以通过 lists.toList.flatten 简化它,但这可能不是重点:)
      • 当然,这就是我在帖子末尾提到它的原因:)
      • lists.flatMap(x => x) == lists.flatten
      • 当然。这也与 OP 实现的相同。这就是重点:我们展示了做同一件事的不同方式,至少在概念上是这样,因为所有这些实现在 scala 库中都是完全不同的。
      猜你喜欢
      • 1970-01-01
      • 2023-03-21
      • 1970-01-01
      • 2016-05-12
      • 2014-07-01
      • 2015-02-28
      • 1970-01-01
      • 1970-01-01
      • 2023-03-07
      相关资源
      最近更新 更多