【问题标题】:Expand a Set[Set[String]] into Cartesian Product in Scala在 Scala 中将 Set[Set[String]] 展开为笛卡尔积
【发布时间】:2011-02-22 22:57:14
【问题描述】:

我有以下一组。不知道要等多久。

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")

你会怎么做?

【问题讨论】:

    标签: scala scala-2.8


    【解决方案1】:

    我想我知道该怎么做。

    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)
    

    【讨论】:

    • 同步性!在我的解决方案中,将字符串的构造推迟到最后一步,但它们基本相同。
    【解决方案2】:

    好问题。这是一种方法:

    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)
    

    【讨论】:

    • 谢谢。我首先尝试使用 foldLeft 并最终发现我需要改用 reduceLeft (一直得到一个空结果)。是否只需要转换为 Seq 以保持排序?
    • @huynhjl 使用 seq(开头)可能是为了避免导入 scala.collection.immutable.Set,也许是为了表明这可以通过更通用的界面来完成。
    • Seq vs Set 在第一行并不重要,我只是发现使用Seq 来保持排序更容易。第二行的选择很重要,例如,您可以使用Set 删除重复项。
    【解决方案3】:

    在战斗之后来了;)但是另一个:

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

    【讨论】:

      【解决方案4】:

      扩展dsg's answer,如果你不介意柯里化函数,你可以这样写更清楚(我认为):

      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
      

      【讨论】:

        【解决方案5】:

        扩展至@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
        

        【讨论】:

        猜你喜欢
        • 2021-06-25
        • 1970-01-01
        • 2017-06-02
        • 1970-01-01
        • 2011-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多