【问题标题】:Scala - Pattern Matching and For loop issueScala - 模式匹配和 For 循环问题
【发布时间】:2013-10-29 09:30:02
【问题描述】:

我正在尝试解决S-99: Ninety-Nine Scala Problems的问题12

给定问题 P10 中指定的运行长度代码列表, 构造它的未压缩版本。示例:

scala> decode(List((4, 'a), (1, 'b), (2, 'c), (2, 'a), (1, 'd), (4, 'e)))
res0: List[Symbol] = List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)

我试图对列表中的元素进行模式匹配,然后使用 for 循环连接 char,但在第 5 行出现以下编译错误:

type mismatch;  found   : scala.collection.immutable.IndexedSeq[List[A]]  required: List[A]

1 def decode[A](xs: List[(Int, A)]) : List[A] = xs match {
2     case Nil => Nil
3     case x :: xs => {
4                    for {
5                       i <- 1 to x._1
6                    }  yield (x._2) :: decode(xs)
7                   }
8 }

抱歉,我开始使用 Scala。有人可以解释为什么会发生这种情况以及如何解决它吗?

【问题讨论】:

    标签: scala for-loop pattern-matching


    【解决方案1】:

    你很接近 - 只是几个问题。这是我想出的一个固定版本:

    def decode[A](xs: List[(Int, A)]) : List[A] = xs match {
       case Nil => Nil
       case x :: xs => (for {
                           i <- 1 to x._1
                        } yield (x._2)).toList ::: decode(xs)
    }
    

    第一件事——也可能是最重要的——是在 for-yield 周围的额外括号。如果没有这个,你会尝试产生(x._2) :: decode(xs),而不仅仅是(x._2)(为了弥补这一点,可以省略整个案例周围的{})。

    接下来,for-yield 生成的是 IndexedSeq 而不是 List,因此我强制转换为 List(您可以通过各种方式处理这个问题,这只是最权宜之计)。

    最后,连接到由decode(xs) 产生的列表需要::: 运算符(您也可以使用++)而不是::(它会添加单个条目,而不是子列表)。

    【讨论】:

    • yet another version(不发布内联,因为 github 在 cmets 中的代码格式方面做得很糟糕)
    • 感谢您的回答 =)
    【解决方案2】:

    主要问题是您用于连接列表的运算符 - :: 仅用于将单个元素添加到列表中,因此在您的代码中您尝试添加 yield 的结果(这本身就是一个序列) 到 List[A] 并因此导致类型不兼容。这是一个可以工作的修改版本 - 它使用运算符++: 可用于将两个序列连接在一起。我还将yield 移到了单独的语句中,否则您需要在 yield 周围加上括号,以便++: 处理yield 的完整结果而不是每个元素(由于类型不匹配)。

    def decode[A](xs: List[(Int, A)]) : List[A] = xs match {
      case Nil => Nil
      case x :: xs => {
        val repeatedElems = for {
          i <- 1 to x._1
        }  yield (x._2)
        repeatedElems ++: decode(xs)
      }
    }
    

    【讨论】:

    • 也感谢您的回答! =) 我只能接受一个答案,所以我会支持你的答案 =)
    【解决方案3】:

    其他答案都很好,但我认为使用List.fill 生成解码列表需要的语法更少,并且与for-yield 表达式相比更容易理解。

    def decode[A](xs: List[(Int, A)]) : List[A] = xs match {
       case Nil => Nil
       case x :: xs => List.fill(x._1)(x._2) ::: decode(xs)
    }
    

    输出:

    scala> decode(List((4, 'a), (1, 'b), (2, 'c), (2, 'a), (1, 'd), (4, 'e)))
    res0: List[Symbol] = List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)
    

    【讨论】:

      【解决方案4】:

      这是布赖恩答案的略微修改版本。分解元组使代码更具可读性:

      def decode[A](xs: List[(Int, A)]) : List[A] = xs match {
         case Nil => Nil
         case (count, letter) :: xs => List.fill(count)(letter) ::: decode(xs)
      }
      

      【讨论】:

        【解决方案5】:

        另一种方法是使用地图:

        def decode[A](l: List[(Int, A)]): List[A] = {
          val l1: List[List[A]] = l map { e =>
            List.fill(e._1)(e._2)
          }
          l1.flatten
        } 
        

        【讨论】:

        • flatMap 将更具可读性(根本没有中间变量)
        猜你喜欢
        • 1970-01-01
        • 2016-11-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多