2

我有一些用于 sum 和 product 类型混合的案例类:

sealed trait Leaf
case class GoodLeaf(value: Int) extends Leaf
case object BadLeaf extends Leaf

case class Middle(left: Leaf, right: Leaf)

case class Container(leaf: Leaf)

case class Top(middle : Middle, container: Container, extraLeaves : List[Leaf])

我想用这种Top结构做一些类似折叠的操作。示例包括:

  • 计算出现次数BadLeaf
  • GoodLeaf将s中的所有值相加

这是一些执行操作的代码:

object Top {
  def fold[T](accu: T)(f : (T, Leaf) => T)(top: Top) = {
    val allLeaves =  top.container.leaf :: top.middle.left :: top.middle.right :: top.extraLeaves
    allLeaves.foldLeft(accu)(f)
  }

  private def countBadLeaf(count: Int, leaf : Leaf) = leaf match {
    case BadLeaf => count + 1
    case _ => count
  }

  def countBad(top: Top): Int = fold(0)(countBadLeaf)(top)

  private def sumGoodLeaf(count: Int, leaf : Leaf) = leaf match {
    case GoodLeaf(v) => count + v
    case _ => count
  }

  def sumGoodValues(top: Top) = fold(0)(sumGoodLeaf)(top)
}

我正在处理的现实生活结构比我编造的例子要复杂得多。是否有任何技术可以帮助我避免编写大量样板代码?

我已经将该cats库作为依赖项,因此首选使用该库的解决方案。为了解决这个问题,我愿意包含新的依赖项。

对于我的特定示例,定义不是递归的,但我有兴趣看到一个也适用于递归定义的解决方案。

4

1 回答 1

1

您可以创建一个返回所有叶子的函数Top,就像您对 所做的那样allLeaves,因此您可以只使用 a (使用Scala 库、Cats 等提供的List[Leaf]所有现有函数和其他函数)。fold

例如 :

def topLeaves(top: Top): List[Leaf] =
  top.container.leaf :: top.middle.left :: top.middle.right :: top.extraLeaves

val isBadLeaf: Leaf => Boolean = {
  case BadLeaf => true
  case _       => false
}

val leafValue: Leaf => Int = {
  case GoodLeaf(v) => v
  case _           => 0
}

您可以用作

import cats.implicits._
// or
// import cats.instances.int._
// import cats.instances.list._
// import cats.syntax.foldable._

val leaves = topLeaves(someTop)

val badCount   = leaves.count(isBadLeaf)
val badAndGood = leaves.partition(isBadLeaf) // (List[Leaf], List[Leaf])
val sumLeaves  = leaves.foldMap(leafValue)

我不确定这是否有助于您的实际用例?通常,对于异构结构(例如您的Top),您可能希望以某种方式将其转换为可以折叠的更均匀的结构(例如 aList[Leaf]或)。Tree[Leaf]

如果你有一个递归结构,你可以看看一些关于递归方案的讨论(使用 Scala 中的Matryoshka库)。

于 2016-12-01T15:08:13.840 回答