我正在尝试组合(请告诉我,如果我使用的术语可以改进),这样我们结果列表的每个元素都是两个输入列表的组合,结果列表涵盖所有可能的组合。
不允许元素移动,子列表的第一个元素是任一输入List的第一个元素,子列表的第二个元素是任一子列表的第二个元素,依此类推。
示例1:
输入:List(1,1,1)
和List(0,0,0)
(我们可以假设列表长度相同)
输出:
List(List(1, 1, 1),
List(1, 1, 0),
List(1, 0, 1),
List(0, 1, 1),
List(1, 0, 0),
List(0, 1, 0),
List(0, 0, 1),
List(0, 0, 0))
示例2:
输入:List(1,2,3)
和List(4,5,6)
输出:
List(List(1, 2, 3),
List(1, 2, 6),
List(1, 5, 3),
List(1, 5, 6),
List(4, 5, 6),
List(4, 5, 3),
List(4, 2, 3),
List(4, 2, 6))
我目前的解决方案仅适用于简单案例(示例1):
def makeCombinations(a: List[Int], b: List[Int]): List[List[Int]] = {
val N = a.length
(0 to N).map(x => a.slice(x, N) ::: b.slice(0, x)).flatMap(x => x.permutations).toList
}
最好的方法是什么?
我不希望找到cross product
,这会导致:
List(List(1, 4),
List(1, 5),
List(1, 6),
List(2, 4),
List(2, 5),
List(2, 6),
List(3, 4),
List(3, 5),
List(3, 6))
此问题与以下内容不同:Cross product in Scala
答案 0 :(得分:4)
以下是查看它的一种方法:您希望将输入列表压缩在一起,然后您想要查看结果列表,flatMap
沿途尝试所有组合。在List
上有效执行此操作的方法是使用foldRight
。
def combine[A](xs: List[A], ys: List[A]): List[List[A]] =
(xs zip ys).foldRight(List(List[A]()))(
(xy, zss) => for (z <- List(xy._1, xy._2); zs <- zss) yield (z :: zs)
)
在REPL尝试:
scala> Test.combine(List(1,2,3), List(4,5,6))
res0: List[List[Int]] = List(List(1, 2, 3), List(1, 2, 6), List(1, 5, 3),
List(1, 5, 6), List(4, 2, 3), List(4, 2, 6), List(4, 5, 3), List(4, 5, 6))
答案 1 :(得分:1)
这可能不如foldRight
版本有效,但作为开头更容易理解:
//almost same as your List(0,0,0) List(1,1,1)
def combos(n: Int): List[List[Boolean]] = (1 to n).foldLeft(List(List.empty[Boolean])){
case (acc, _) => acc.map(false :: _) ::: acc.map(true :: _)
}
//apply combo (like 1,1,0) to choose element from either of lists
def applyCombo(a: List[Int], b: List[Int])(combo: List[Boolean]): List[Int] =
a zip b zip combo map { case ((x, y), b) => if (b) x else y }
//get all combinations (as boolean) and apply them to a and b
def combine(a: List[Int], b: List[Int]) = combos(a.size) map applyCombo(a, b)
将它放在一起,摆脱中间List[List[Boolean]]
并添加foldRight
:
def combine(aL: List[Int], bL: List[Int]) = (aL zip bL).foldRight(List(List.empty[Int])){
case ((a, b), acc) => acc.map(a :: _) ::: acc.map(b :: _)
}
实验:
a: List[Int] = List(1, 2, 3)
b: List[Int] = List(4, 5, 6)
res73_3: List[List[Int]] = List(
List(1, 2, 3),
List(1, 2, 6),
List(1, 5, 3),
List(1, 5, 6),
List(4, 2, 3),
List(4, 2, 6),
List(4, 5, 3),
List(4, 5, 6)
)
注意:
:::
效率不高,如果您不关心组合的顺序,可以将其替换为acc.flatMap(x => List(a :: x, b :: x))
。