在Scala中将Set [Set [String]]展开为Cartesian Product

时间:2010-05-23 15:37:00

标签: scala scala-2.8

我有以下几套。我不知道提前多久。

val sets = Set(Set("a","b","c"), Set("1","2"), Set("S","T"))

我想将其扩展为笛卡尔积:

Set("a&1&S", "a&1&T", "a&2&S", ..., "c&2&T")

你会怎么做?

5 个答案:

答案 0 :(得分:17)

我想我知道该怎么做。

def combine(acc:Set[String], set:Set[String]) = for (a <- acc; s <- set) yield {
  a + "&" + s 
}

val expanded = sets.reduceLeft(combine)

expanded: scala.collection.immutable.Set[java.lang.String] = Set(b&2&T, a&1&S, 
  a&1&T, b&1&S, b&1&T, c&1&T, a&2&T, c&1&S, c&2&T, a&2&S, c&2&S, b&2&S)

答案 1 :(得分:12)

好问题。这是一种方式:

scala> val seqs = Seq(Seq("a","b","c"), Seq("1","2"), Seq("S","T"))                  
seqs: Seq[Seq[java.lang.String]] = List(List(a, b, c), List(1, 2), List(S, T))

scala> val seqs2 = seqs.map(_.map(Seq(_)))
seqs2: Seq[Seq[Seq[java.lang.String]]] = List(List(List(a), List(b), List(c)), List(List(1), List(2)), List(List(S), List(T)))

scala> val combined = seqs2.reduceLeft((xs, ys) => for {x <- xs; y <- ys} yield x ++ y)
combined: Seq[Seq[java.lang.String]] = List(List(a, 1, S), List(a, 1, T), List(a, 2, S), List(a, 2, T), List(b, 1, S), List(b, 1, T), List(b, 2, S), List(b, 2, T), List(c, 1, S), List(c, 1, T), List(c, 2, S), List(c, 2, T))

scala> combined.map(_.mkString("&"))             
res11: Seq[String] = List(a&1&S, a&1&T, a&2&S, a&2&T, b&1&S, b&1&T, b&2&S, b&2&T, c&1&S, c&1&T, c&2&S, c&2&T)

答案 2 :(得分:6)

战斗结束后;)但另一个:

sets.reduceLeft((s0,s1)=>s0.flatMap(a=>s1.map(a+"&"+_)))

答案 3 :(得分:3)

扩展@Patrick's answer。 现在它更通用,更懒惰:

def combine[A](f:(A, A) => A)(xs:Iterable[Iterable[A]]) =
    xs.reduceLeft { (x, y) => x.view.flatMap {a => y.map(f(a, _)) } } 

让它变得懒惰可以节省空间,因为你不会在扩展集中存储指数级的项目;相反,你可以动态生成它们。但是,如果你真的想要全套,你仍然可以这样:

val expanded = combine{(x:String, y:String) => x + "&" + y}(sets).toSet

答案 4 :(得分:3)

扩展dsg's answer,你可以用这种方式写清楚(我认为),如果你不介意curried函数:

def combine[A](f: A => A => A)(xs:Iterable[Iterable[A]]) =
   xs reduceLeft { (x, y) => x.view flatMap { y map f(_) } }

另一种选择(稍长,但更具可读性):

def combine[A](f: (A, A) => A)(xs:Iterable[Iterable[A]]) =
   xs reduceLeft { (x, y) => for (a <- x.view; b <- y) yield f(a, b) }

用法:

combine[String](a => b => a + "&" + b)(sets)   // curried version

combine[String](_ + "&" + _)(sets)             // uncurried version