我正在研究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)表示众所周知的二项式系数)。对于纯数学家来说,这个结果可能很棒。但我们希望真正产生所有可能性。
实施例: 阶&GT;组合(3,列表('a,'b,'c,'d,'e,'f)) res0:List [List [Symbol]] = List(List('a,'b,'c),List('a,'b,'d),List('a,'b,'e),..
答案 0 :(得分:2)
正如诺亚指出的那样,我的问题是for
空列表不会产生。然而,诺亚提出的黑客工作是错误的。它为每个递归步骤的结果添加一个空列表。无论如何,这是我的最终解决方案。我将基本案例改为“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))
答案 1 :(得分:2)
在这里翻译Haskell版本时你犯了一个错误:
case (0, _) => List[List[T]]()
这将返回一个空列表。而Haskell版本
combinations 0 _ = [ [] ]
返回一个包含单个元素的列表,该元素是一个空列表。
这实质上是说有一种方法可以选择零项,这很重要,因为在我们选择更多项的情况下,代码会递归地建立在这种情况下。如果没有办法选择零项目,那么也没有办法选择一个项目,依此类推。这就是您的代码中发生的事情。
如果您修复Scala版本与Haskell版本相同:
case (0, _) => List(List[T]())
它按预期工作。
答案 2 :(得分:1)
您的问题是使用for
对列表的理解。如果for
检测到一个空列表,那么它会短路并返回一个空列表而不是“消耗”你的头元素。这是一个例子:
scala> for { xs <- List() } yield println("It worked!") // This never prints
res0: List[Unit] = List()
因此,对于你的组合函数来说,一种hacky工作方式是:
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))
答案 3 :(得分:0)
解决它的另一种方法。
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))