2016-11-10 8 views
2

У меня есть некоторые классы случая для сочетания суммы и произведения типов:Traverse/фолд вложенного класса дела в Scala без шаблонного кода

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 как зависимость, поэтому было бы предпочтительным решение, использующее эту библиотеку. Я открыт для включения новых зависимостей для решения этой проблемы.

Для моего конкретного примера определение не является рекурсивным, но мне было бы интересно увидеть решение, которое также работает для рекурсивных определений.

+1

* Я знаю, как писать код для этого, но это будет утомительно. * Сначала покажите шаблонный паттерн; потом поговорим. – Jubobs

ответ

1

Вы можете просто создать функцию, возвращающую все листья для Top, как вы сделали с allLeaves, так что вы можете работать с List[Leaf] (со всеми существующими fold и другими функциями библиотеки Scala, кошки и т.д. предоставляют).

Например:

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]), где вы можете сбросить карты.

Если у вас есть рекурсивная структура, вы можете посмотреть некоторые разговоры о схемах рекурсии (с библиотекой Matryoshka в Scala).