如何将ADT列表分为不同的变体?

时间:2018-11-28 16:52:08

标签: scala functional-programming scalaz scala-cats

是否可以通过某种方式将解决方案扩展为求和类型?

sealed trait Group
case class A extends Group
case class B extends Group
case class C extends Group
def divide(l : List[Group]): //Something from what I can extract List[A], List[B] and List[C]

2 个答案:

答案 0 :(得分:1)

也许您可以尝试改善此答案。这可能无法解决您的问题,因为很难知道给定类型的任意子类型(Group类型可能具有任意数量的子类型)。对于Either,很容易预测其子类型为RightLeft

sealed trait Group
case class A(name:String) extends Group
case class B(name:String) extends Group
case class C(name:String) extends Group

val list = List(
                A("a1"), A("a2"), A("a3"), A("a4"), 
                B("b1"), B("b2"), B("b3"), B("b4"), 
                C("c1"), C("c2"), C("c3"), C("c4")
                )  

def divide(
   list: List[Group],
   aList : List[A], 
   bList: List[B], 
   cList: List[C]
): (List[A], List[B], List[C]) = {
  list match {
    case Nil => (aList, bList, cList)
    case head :: tail => head match {
      case a : A => divide(tail, aList.:+(a), bList, cList)
      case b : B =>  divide(tail,aList, bList.:+(b), cList)
      case c : C => divide(tail, aList, bList, cList.:+(c))
    }
  }
}

divide(list, List.empty[A], List.empty[B], List.empty[C])
//res1: (List[A], List[B], List[C]) = (List(A(a1), A(a2), A(a3), A(a4)),List(B(b1), B(b2), B(b3), B(b4)),List(C(c1), C(c2), C(c3), C(c4)))

希望这对您有所帮助。

答案 1 :(得分:0)

我自己使用Shapeless完成了该任务:

import shapeless.{:+:, ::, CNil, Coproduct, Generic, HList, HNil}

/*
Suppose we have a sealed trait and few implementations:
sealed trait Animal
case class Cat(a: Int) extends Animal
case class Dog(b: Int) extends Animal
case class Fox(c: Int) extends Animal

and a list:
val animals: List[Animal]

how to split the list into sub-lists per a subclass?
val cats: List[Cat] = ???
val dogs: List[Dog] = ???
val foxes: List[Fox] = ???

Of course it must work w/o boilerplate for arbitrary numbers of children
 */
object Split {

  trait Splitter[T <: Coproduct] {
    type R <: HList

    def split(list: List[T]): R

  }

  type Aux[T <: Coproduct, R0 <: HList] = Splitter[T] {
    type R = R0
  }

  implicit val cNilSplitter = new Splitter[CNil] {
    type R = HNil

    override def split(list: List[CNil]): HNil = HNil
  }

  implicit def cPllusSplitter[H, T <: Coproduct, R <: HList](implicit ev: Aux[T, R]): Aux[H :+: T, List[H] :: ev.R] = new Splitter[H :+: T] {
    type R = List[H] :: ev.R

    override def split(list: List[H :+: T]): ::[List[H], ev.R] = {
      val heads: List[H] = list.flatMap(e => e.eliminate(h => Some(h), t => None))
      val tails: List[T] = list.flatMap(e => e.eliminate(h => None, t => Some(t)))
      val sub: ev.R = ev.split(tails)
      heads :: sub
    }
  }

  def splitCoproduct[T <: Coproduct, R <: HList](list: List[T])(implicit ev: Aux[T, R]): R = ev.split(list)

  def split[X, T <: Coproduct, R <: HList](list: List[X])(implicit gen: Generic.Aux[X, T], ev: Aux[T, R]): R = {
    val asCoproduct: List[T] = list.map(gen.to)
    splitCoproduct[T, R](asCoproduct)(ev)
  }

}


object Runner {

  import Split._

  def main(args: Array[String]): Unit = {

    sealed trait Animal
    case class Cat(a: Int) extends Animal
    case class Dog(b: Int) extends Animal
    case class Fox(c: Int) extends Animal

    val animals: List[Animal] = List(Cat(1), Dog(1), Cat(2), Fox(1), Dog(2), Dog(3))
    val result = split(animals) //List[Cat] :: List[Dog] :: List[Fox] :: HNil
    println(result)
    val cats: List[Cat] = result.head
    val dogs: List[Dog] = result.tail.head
    val foxes: List[Fox] = result.tail.tail.head
    println(cats)
    println(dogs)
    println(foxes)
  }
}