【问题标题】:Flattening a Set of pairs of sets to one pair of sets将一组成对的集合展平为一对集合
【发布时间】:2013-11-29 02:27:55
【问题描述】:

我有一个 for-comprehension 和来自 Set[MyType] 的生成器 这个MyType 有一个名为factsPairlazy val 变量,它返回一对集合: (设置[MyFact],设置[MyFact])。

我希望遍历所有这些并将事实统一为一个扁平的对 (Set[MyFact], Set[MyFact]),如下所示,但是我收到 No implicit view available ...not enough arguments for flatten: implicit (asTraversable ... 错误。 (我对 Scala 有点陌生,所以仍在努力适应错误)。

lazy val allFacts  =
(for {
  mytype <- mytypeList
} yield mytype.factsPair).flatten

我需要指定什么来使这个工作变平?

【问题讨论】:

    标签: scala flatten for-comprehension


    【解决方案1】:

    元组是不可遍历的,所以你不能在它上面展平。你需要返回一些可以迭代的东西,比如一个 List,例如:

    List((1,2), (3,4)).flatten          // bad
    List(List(1,2), List(3,4)).flatten  // good
    

    【讨论】:

    • 谢谢。我有Set((Set(1,2),Set(3,4)), (Set(5,6),Set(7,8))),我想要的结果是(Set(1,2,5,6), Set(3,4,7,8))。我想所有的嵌套都让我感到困惑。
    【解决方案2】:

    Scala flatten 适用于相同的类型。你有一个 Seq[(Set[MyFact], Set[MyFact])],它不能被展平。

    我建议学习foldLeft 函数,因为它非常通用,而且一旦你掌握了它就很容易使用:

    lazy val allFacts = myTypeList.foldLeft((Set[MyFact](), Set[MyFact]())) {
      case (accumulator, next) =>
        val pairs1 = accumulator._1 ++ next.factsPair._1
        val pairs2 = accumulator._2 ++ next.factsPair._2
        (pairs1, pairs2)
    }
    

    第一个参数采用初始元素,它将附加其他元素。我们从一个空的Tuple[Set[MyFact], Set[MyFact]] 开始,初始化如下:(Set[MyFact](), Set[MyFact]())

    接下来,我们必须指定接收累加器并将下一个元素附加到它的函数,并返回包含下一个元素的新累加器。由于所有的元组,它看起来不太好,但有效。

    【讨论】:

    • 谢谢。你的回答有点冗长,但我还是能理解发生了什么。
    • @jbx 这不仅仅是冗长的,而是一次完成,而不是多次完成。这通常是foldLeft 的优势在于您可以将任意多的魔法写入身体:)
    【解决方案3】:

    您将无法为此使用flatten,因为集合上的flatten 返回一个集合,而元组不是一个集合。

    当然,您可以拆分、展平并再次加入:

    val pairs = for {
      mytype <- mytypeList
    } yield mytype.factsPair
    val (first, second) = pairs.unzip
    val allFacts = (first.flatten, second.flatten)
    

    【讨论】:

    • 或:val (first, second) = pairs.unzip?
    • 感谢您的回答!
    • @dhg 所以我实际上可以在我的解决方案中将flatten 更改为unzip?我已经有一组经过理解的配对,所以如果我理解正确,解压缩会将它们组合成一对?
    • @dhg 我忘记了unzip。感谢您的提示!
    【解决方案4】:

    我想提供一个更代数的观点。使用monoids 可以很好地解决您在这里的问题。对于每个幺半群,都有一个零元素和一个将两个元素合二为一的操作。

    在这种情况下,一个幺半群的集合:零元素是一个空集,操作是一个联合。如果我们有两个幺半群,它们的笛卡尔积也是一个幺半群,其中操作是成对定义的(参见examples on Wikipedia)。

    Scalaz 为集合和元组定义了幺半群,所以我们不需要在那里做任何事情。我们只需要一个辅助函数,将多个幺半群元素组合成一个,使用折叠很容易实现:

    def msum[A](ps: Iterable[A])(implicit m: Monoid[A]): A =
      ps.foldLeft(m.zero)(m.append(_, _))
    

    (可能Scala里已经有这样的功能了,我没找到)。使用msum,我们可以轻松定义

    def pairs(ps: Iterable[MyType]): (Set[MyFact], Set[MyFact]) =
      msum(ps.map(_.factsPair))
    

    对元组和集合使用 Scalaz 的隐式幺半群。

    【讨论】:

      猜你喜欢
      • 2022-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-29
      • 2011-07-23
      • 2021-11-24
      • 2014-07-10
      相关资源
      最近更新 更多