Scala组合任意数量的集合元素

问题描述 投票:0回答:4

假设有三个集合:

val numbers = List("1", "2")
val signs = List("-", "+")
val chars = List("a", "b")

我想生成这些集合的元素组合。我想要的不仅仅是笛卡尔积,也不是所有可能的组合。我想拥有的是这样的:

(1)
(1, -)
(1, -, a)
(1, -, b)
(1, +)
(1, +, a)
(1, +, b)
...

如果我可以在一组公式中总结一下,我想要这些集合:

numbers
signs
chars
numbers * signs
numbers * chars
signs * chars
numbers * signs * chars

重要的是要注意每个产品只能包含每个产品中的一个元素。例如,这些元组在我的结果中不是我想要的东西:

(1, 2, -)
(a, -, +)

因为他们有两个数字或两个标志。

关于如何处理这个有趣问题的任何提示?我认为Python包itertools有product函数处理这个,但我找不到任何类似的Scala。

scala
4个回答
1
投票

我猜你想要的是这些集合中所有可能的元素子集,按顺序而不是重复。你可以做同样的事情:

val res: List[List[String]] = (for (x <- numbers) yield List(x)) ++
  (for { x <- numbers; y <- signs } yield List(x,y)) ++
    (for { x <- numbers; y <- signs; z <- chars } yield List(x, y, z))

基本上,它是@jwvh和@Dima的答案的混合。如果要获取元组而不是列表,可以执行以下操作:

res.map(s => s match {
  case List(c) => (c)
  case List(x, y) => (x, y)
  case List(x,y,z) => (x,y,z)
  case _ => (s)
})

输出:

scala> res.map(s => s match { case List(c) => (c); case List(x, y) =>
(x,y); case List(x,y,z) => (x,y,z); case _ => (s) })
res21: List[java.io.Serializable] = List(1, 2, (1,-), (1,+), (2,-), (2,+),
(1,-,a), (1,-,b), (1,+,a), (1,+,b), (2,-,a), (2,-,b), (2,+,a), (2,+,b))

回想一下,这个解决方案非常适合您的问题。


0
投票

scala中的“Product”看起来像这样:

 for {
  n <- numbers
  s <- signs
  c <- chars
 } yield (n, s, c)

这应该有助于你有希望开始。


0
投票

您的请求的一个问题是不同大小的元组实际上是不同的类型,因此您不希望将它们混合在一个集合中。

使用List[List[String]]来表达结果,我认为这可以达到你想要的效果。

val numbers = List("1", "2")
val signs = List("-", "+")
val chars = List("a", "b")

numbers.flatMap{n =>
  List(n) :: signs.flatMap{s =>
    List(s) :: List(n,s) :: chars.flatMap{c =>
      List(List(c), List(n,c), List(s,c), List(n,s,c))
    }
  }
}.distinct

0
投票

谢谢大家的提示,我设法解决了这个问题。这就是我做的...

首先我定义了一组集合名称,让我们这样称呼它们:

val set: Set[String] = Set("numbers", "signs", "chars")

除此之外,我还定义了它们的值:

val valueMap: Map[String, List[String]] = Map("numbers" -> List("1", "2", "3"), "signs" -> List("+", "-"), "chars" -> List("a", "b")

然后我做了一些映射和魔术:

val kpiComboPhase1 = set.subsets.toList.map(aSet => {
  aSet.toList.flatMap(el => valueMap.get(el))
})
val kpiComboPhase2 = kpiComboPhase1.map(l => {
  if (l.length === 1) l.flatten else l
})

这有助于我得到这样的东西:

Set()
Set(numbers)
Set(signs)
Set(chars)
Set(numbers, signs)
Set(numbers, chars)
Set(signs, chars)
Set(numbers, signs, chars)

在那之后,我使用了来自valueMap的每个集合的值(每个值都是List[String]并应用这种方法https://stackoverflow.com/a/42095955/589571来制作任意数量的列表的递归交叉产品。我需要更多的映射和体操来获得我想要的结构但总的来说,就是这样。

© www.soinside.com 2019 - 2024. All rights reserved.