【问题标题】:my combinations function returns an empty list我的组合函数返回一个空列表
【发布时间】:2013-06-09 00:46:29
【问题描述】:

我正在处理S-99: Ninety-Nine Scala Problems 并且已经卡在问题 26 上。 生成从列表的 N 个元素中选择的 K 个不同对象的组合。 浪费了几个小时后,我决定看一下用 Haskell 编写的解决方案:

combinations :: Int -> [a] -> [[a]]
combinations 0 _  = [ [] ]
combinations n xs = [ y:ys | y:xs' <- tails xs
                       , ys <- combinations (n-1) xs']

它看起来很简单,所以我决定翻译成 Scala。 (我知道那是作弊。)这是我目前得到的:

def combinations[T](n: Int, ls: List[T]): List[List[T]] = (n, ls) match {
case (0, _) => List[List[T]]()
case (n, xs) => {
  for {
    y :: xss <- allTails(xs).reverse
    ys <- combinations((n - 1), xss)
  } yield y :: ys
}
} 

我的辅助函数:

def allTails[T](ls: List[T]): List[List[T]] = {
ls./:(0, List[List[T]]())((acc, c) => {
  (acc._1 + 1, ls.drop(acc._1) :: acc._2)
})._2 }

allTails(List(0, 1, 2, 3)).reverse              
//> res1: List[List[Int]] = List(List(0, 1, 2, 3), List(1, 2, 3), List(2, 3), List(3))

但是,我的组合返回一个空列表。任何想法? 其他带有解释的解决方案也非常受欢迎。谢谢

编辑:问题的描述

生成从列表的 N 个元素中选择的 K 个不同对象的组合。 从 12 个人中选出一个 3 人的委员会有几种方法?我们都知道有 C(12,3) = 220 种可能性(C(N,K) 表示众所周知的二项式系数)。对于纯数学家来说,这个结果可能很棒。但我们想真正产生所有的可能性。

示例: scala>组合(3,列表('a,'b,'c,'d,'e,'f)) res0: List[List[Symbol]] = List(List('a, 'b, 'c), List('a, 'b, 'd), List('a, 'b, 'e), .. .

【问题讨论】:

    标签: scala functional-programming


    【解决方案1】:

    正如诺亚指出的那样,我的问题是 for 的空列表不会产生。然而,Noah 建议的解决方法是错误的。它在每个递归步骤的结果中添加一个空列表。无论如何,这是我的最终解决方案。我将基本情况更改为“case (1, xs)”。 (n 匹配 1)

    def combinations[T](n: Int, ls: List[T]): List[List[T]] = (n, ls) match {
    case (1, xs) =>  xs.map(List(_))
    case (n, xs) => {
      val tails = allTails(xs).reverse
      for {
        y :: xss <- allTails(xs).reverse
        ys <- combinations((n - 1), xss)
      } yield y :: ys
    }
    }
    //combinations(3, List(1, 2, 3, 4))
    //List(List(1, 2, 3), List(1, 2, 4), List(1, 3, 4), List(2, 3, 4))
    //combinations(2, List(0, 1, 2, 3))
    //List(List(0, 1), List(0, 2), List(0, 3), List(1, 2), List(1, 3), List(2, 3))
    
    def allTails[T](ls: List[T]): List[List[T]] = {
    ls./:(0, List[List[T]]())((acc, c) => {
      (acc._1 + 1, ls.drop(acc._1) :: acc._2)
    })._2
    }
    //allTails(List(0,1,2,3)) 
    //List(List(3), List(2, 3), List(1, 2, 3), List(0, 1, 2, 3))
    

    【讨论】:

      【解决方案2】:

      您在这里翻译 Haskell 版本时犯了一个错误:

      case (0, _) => List[List[T]]()
      

      这将返回一个空列表。而 Haskell 版本

      combinations 0 _  = [ [] ]
      

      返回一个包含单个元素的列表,该元素是一个空列表。

      这本质上是说有一种方法可以选择零项,这很重要,因为对于我们选择更多项的情况,代码递归地构建在这种情况下。如果没有办法选择零项,那么也就没有办法选择一项,以此类推。这就是您的代码中发生的情况。

      如果您将 Scala 版本修复为与 Haskell 版本相同:

      case (0, _) => List(List[T]())
      

      它按预期工作。

      【讨论】:

      • 不敢相信以前没有人知道。非常感谢!
      • @EntryLevelDev:我自己在 Haskell 中编写类似代码时也犯了同样的错误,所以在阅读标题后,我首先要寻找它。
      【解决方案3】:

      您的问题是将for 理解与列表一起使用。如果for 检测到一个空列表,那么它会短路并返回一个空列表,而不是'cons'你的 head 元素。这是一个例子:

      scala> for { xs <- List() } yield println("It worked!") // This never prints
      res0: List[Unit] = List()
      

      因此,您的组合功能的一种解决方法是:

        def combinations[T](n: Int, ls: List[T]): List[List[T]] = (n, ls) match {
          case (0, _) => List[List[T]]()
          case (n, xs) => {
            val tails = allTails(xs).reverse
            println(tails)
            for {
              y :: xss <- tails
              ys <- Nil :: combinations((n - 1), xss) //Now we're sure to keep evaulating even with an empty list
            } yield y :: ys
          }
        }
      
      scala> combinations(2, List(1, 2, 3))
      List(List(1, 2, 3), List(2, 3), List(3))
      List(List(2, 3), List(3))
      List(List(3))
      List()
      res5: List[List[Int]] = List(List(1), List(1, 2), List(1, 3), List(2), List(2, 3), List(3))
      

      【讨论】:

      • 谢谢。现在它返回了一些东西。但是,结果不正确。
      【解决方案4】:

      另一种解决方法。

      def combinations[T](n: Int, ls: List[T]): List[List[T]] = {
      var ms: List[List[T]] = List[List[T]]();
      val len = ls.size
      if (n > len)
        throw new Error();
      else if (n == len)
        List(ls)
      else if (n == 1)
        ls map (a => List(a))
      else {
        for (i <- n to len) {
          val take: List[T] = ls take i;
          val temp = combinations(n - 1, take.init) map (a => take.last :: a)
          ms = ms ::: temp
        }
        ms
      }
      }
      

      所以combinations(2, List(1, 2, 3)) 给出:List[List[Int]] = List(List(2, 1), List(3, 1), List(3, 2))

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-12-22
        • 1970-01-01
        • 2016-03-14
        • 1970-01-01
        • 1970-01-01
        • 2014-01-08
        • 1970-01-01
        • 2019-04-23
        相关资源
        最近更新 更多