我有一些用于混合总和和产品类型的案例类:
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
库作为依赖项,因此首选使用该库的解决方案。我愿意包含新的依赖项以解决这个问题。
对于我的特定示例,该定义不是递归的,但我也有兴趣看到一个适用于递归定义的解决方案。
答案 0 :(得分:1)
您可以创建一个函数,返回Top
的所有叶子,就像使用allLeaves
一样,这样您就可以使用List[Leaf]
(包含所有现有fold
和Scala库,Cats等提供的其他功能。)
例如:
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
),您可能希望以某种方式将其转换为更加齐同的内容(如List[Leaf]
或Tree[Leaf]
),您可以将其折叠起来。
如果你有一个递归结构,你可以看一些关于递归方案的讨论(在Scala中使用Matryoshka库)。