关于在scala中使用List(List [A]())和List [List [A]]()的不同情况?

时间:2013-12-31 15:04:55

标签: scala

我正在尝试scala-99 P09。

作者给出的答案是:

object P09 {
  def pack[A](ls: List[A]): List[List[A]] = {
    if (ls.isEmpty) List(List())
    else {
      val (packed, next) = ls span { _ == ls.head }
      if (next == Nil) List(packed)
      else packed :: pack(next)
    }
  }
}

它给出了:

pack(List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e))
// res0: List[List[Symbol]] = List(List('a, 'a, 'a, 'a), List('b), List('c, 'c), List('a, 'a), List('d), List('e, 'e, 'e, 'e))

pack(List[Int]()) // => res1: List[List[Int]] = List(List())

但是,我认为它应该是List[List[Int]] = List()

出于什么原因,作者返回List(List())而不是List()

今天,我遇到了P26(生成从列表的N个元素中选择的K个不同对象的组合。),我首先给出了以下解决方案:

def combinationsOf[A](n: Int, ls: List[A]): List[List[A]] = {
  if (n < 0 || n > ls.length) throw new IllegalArgumentException
  else if (n == 0) List.empty[List[A]]
  else if (n == ls.length) List(ls)
  else combinationsOf(n - 1, ls.tail).map(ls.head :: _) ::: combinationsOf(n, ls.tail)
}

但是当给定K = 3和List(1,2,3,4)时,它只输出(1,2,4), (1,3,4), (2,3,4),这是错误的。我知道我的代码有什么问题。所以我做对了:

def combinationsOf[A](n: Int, ls: List[A]): List[List[A]] = {
  if (n < 0 || n > ls.length) throw new IllegalArgumentException
  else if (n == 0) List(Nil)
  else if (n == ls.length) List(ls)
  else combinationsOf(n - 1, ls.tail).map(ls.head :: _) ::: combinationsOf(n, ls.tail)
}

我无法理解的东西仍然存在。当List()似乎更合适时,为什么需要List(Nil)

2 个答案:

答案 0 :(得分:1)

我不知道。我还认为如果输入列表为空,它应该返回一个空列表。 我会这样写的

def pack[A](ls: List[A]): List[List[A]] = {
  if (ls.isEmpty) List.empty[List[A]]
  else {
    val (packed, next) = ls span { _ == ls.head }
    packed :: pack(next)
  }
}

甚至更短

def pack[A](ls: List[A]): List[List[A]] = ls.groupBy(identity).values.toList

答案 1 :(得分:1)

来自Ninety-Nine Scala Problems页面:

  

这些改编自Werner Hett在瑞士伯尔尼伯尔尼应用科学大学撰写的Ninety-Nine Prolog Problems。我(Phil Gold)已经改变了它们以便更适合在Scala中编程。我们非常感谢您的反馈意见,尤其是标记为TODO的任何内容。

现在,如果你检查Ninety-Nine Prolog Problems,你会发现问题1.09基本上是这里列出的问题。 Prolog Problems网站还提供解决方案,如果您选中the solution for 1.09,您会看到:

pack([],[]).

我不是一个prolog程序员,但我很确定这意味着作为输入的空列表会导致一个空列表作为输出。如果输入是空列表,则此问题的99 Lisp Problems99 Haskel Problems解决方案也会提供空列表(不是包含单个空列表的列表)。

我认为答案中的List(List())表达看起来好像是因为混淆了如何创建一个空的列表列表。正如@SpiderPig在他修改的解决方案中所示,返回List()而不是List(List())实际上对简化递归逻辑做了很多工作。

99 Scala Problems页面有一个提供反馈的链接,因此您可以随时指出此问题并建议修复。


  

我无法理解的东西仍然存在。当List()似乎更合适时,为什么需要List(Nil)?

在函数的最后一种情况下,map覆盖所有递归调用的结果。如果您的递归调用返回List(),那么您将映射一个空列表,该列表将始终返回一个空列表。相反,如果map(ls.head :: _)List(ls.head)而不是_List(Nil)将返回List()。您可以通过为 n = 1

添加另一个案例来解决此问题
def combinationsOf[A](n: Int, ls: List[A]): List[List[A]] = {
  require(0 <= n && n <= ls.length) // using require is probably better style
  if (n == 0) List()
  else if (n == 1) ls.map(List(_)) // fixes map over empty list problem
  else if (n == ls.length) List(ls)
  else combinationsOf(n - 1, ls.tail).map(ls.head :: _) ::: combinationsOf(n, ls.tail)
}

另请注意,我没有明确抛出IllegalArgumentException,而是将第一个案例与require交换(如果该子句为假,则会抛出IllegalArgumentException