2016-11-10 1 views
2

J'ai quelques classes de cas pour un mélange de somme et de types de produits:Traverse/plier une classe de cas imbriqué dans Scala sans le code boilerplate

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

Je veux faire des opérations comme pli avec cette structure Top. Voici quelques exemples:

  • compter les occurrences de BadLeaf
  • Somme toutes les valeurs dans le GoodLeaf s

Voici un code qui effectue les opérations:

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

La vie réelle la structure à laquelle je fais face est nettement plus compliquée que l'exemple que j'ai inventé. Y at-il des techniques qui pourraient m'aider à éviter d'écrire beaucoup de code standard?

J'ai déjà la bibliothèque cats en tant que dépendance, donc une solution qui utilise cette bibliothèque serait préférable. Je suis ouvert à l'inclusion de nouvelles dépendances afin de résoudre ce problème.

Pour mon exemple particulier, la définition n'est pas récursive, mais je serais intéressé de voir une solution qui fonctionne également pour les définitions récursives.

+1

* Je sais comment écrire du code pour faire ceci, mais ce sera fastidieux. * Montrer d'abord le passe-partout en question; ensuite nous parlerons. – Jubobs

Répondre

1

Vous pouvez simplement créer une fonction renvoyant toutes les feuilles pour Top, comme vous l'avez fait avec allLeaves, de sorte que vous pouvez simplement travailler avec un List[Leaf] (avec toutes les fold et d'autres fonctions existantes de la bibliothèque Scala, chats, etc fournir).

Par exemple:

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 
} 

que vous pourriez utiliser comme

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) 

Je ne sais pas si cela aide votre cas réel d'utilisation? En général, avec une structure hétérogène (comme votre Top), vous voulez probablement le convertir en quelque chose de plus homogène (comme un List[Leaf] ou Tree[Leaf]) où vous pouvez vous replier.

Si vous disposez d'une structure récursive, vous pouvez consulter quelques exemples de schémas de récursivité (avec la bibliothèque Matryoshka dans Scala).