scala 是否将代数日期类型的列表拆分为分支列表?

k4emjkb1  于 2022-11-09  发布在  Scala
关注(0)|答案(1)|浏览(119)

我对Shape还很陌生,所以这个问题可能很简单。
以下是ADT:

sealed trait Test

final case class A() extends Test
final case class B() extends Test
final case class C() extends Test
...
final case class Z() extends Test

有没有可能在没有极其繁琐模式匹配的情况下编写函数?

def split(lst: List[Test]): List[A] :: List[B] :: ... :: HNil = //
rmbxnbpk

rmbxnbpk1#

在编译时,List的所有元素都具有相同的静态类型Test,因此无法区分元素ABC...仅使用编译时技术(无形状、类型类、隐式、宏、编译时反射)。这些元素仅在运行时可区分,因此您必须使用一些运行时技术(模式匹配、强制转换、运行时反射)。
Why Does This Type Constraint Fail for List[Seq[AnyVal or String]]
Scala: verify class parameter is not instanceOf a trait at compile time
flatMap with Shapeless yield FlatMapper not found
尝试使用运行时反射将split添加到Map中

def split(lst: List[Test]): Map[String, List[Test]]  =
  lst.groupBy(_.getClass.getSimpleName)

split(List(C(), B(), A(), C(), B(), A()))
// HashMap(A -> List(A(), A()), B -> List(B(), B()), C -> List(C(), C()))

或使用非成形+运行时反射将split转换为HList

import shapeless.labelled.{FieldType, field}
import shapeless.{::, Coproduct, HList, HNil, LabelledGeneric, Poly1, Typeable, Witness}
import shapeless.ops.coproduct.ToHList
import shapeless.ops.hlist.Mapper
import shapeless.ops.record.Values
import shapeless.record._
import scala.annotation.implicitNotFound

object listPoly extends Poly1 {
  implicit def cse[K <: Symbol, V]: Case.Aux[FieldType[K, V], FieldType[K, List[V]]] = null
}

// modified shapeless.ops.maps.FromMap
@implicitNotFound("Implicit not found: FromMapWithDefault[${R}]. Maps can only be converted to appropriate Record types.")
trait FromMapWithDefault[R <: HList] extends Serializable {
  // if no value by this key use default, if can't cast return None
  def apply[K, V](m: Map[K, V], default: V): Option[R]
}
object FromMapWithDefault {
  implicit def hnilFromMap[T]: FromMapWithDefault[HNil] =
    new FromMapWithDefault[HNil] {
      def apply[K, V](m: Map[K, V], default: V): Option[HNil] = Some(HNil)
    }

  implicit def hlistFromMap[K0, V0, T <: HList]
  (implicit wk: Witness.Aux[K0], tv: Typeable[V0], fmt: FromMapWithDefault[T]): FromMapWithDefault[FieldType[K0, V0] :: T] =
    new FromMapWithDefault[FieldType[K0, V0] :: T] {
      def apply[K, V](m: Map[K, V], default: V): Option[FieldType[K0, V0] :: T] = {
        val value = m.getOrElse(wk.value.asInstanceOf[K], default)
        for {
          typed <- tv.cast(value)
          rest <- fmt(m, default)
        } yield field[K0](typed) :: rest
      }
    }
}

def split[T, C <: Coproduct, L <: HList, L1 <: HList](lst: List[T])(
  implicit
  labelledGeneric: LabelledGeneric.Aux[T, C],
  toHList: ToHList.Aux[C, L],
  mapper: Mapper.Aux[listPoly.type, L, L1],
  fromMapWithDefault: FromMapWithDefault[L1],
  values: Values[L1]
): values.Out = {
  val groupped = lst.groupBy(_.getClass.getSimpleName).map { case (k, v) => Symbol(k) -> v }
  fromMapWithDefault(groupped, Nil).get.values
}

测试:

sealed trait Test
final case class A() extends Test
final case class B() extends Test
final case class C() extends Test
final case class Z() extends Test

val res = split(List[Test](C(), B(), A(), C(), B(), A())) 
// List(A(), A()) :: List(B(), B()) :: List(C(), C()) :: List() :: HNil
res: List[A] :: List[B] :: List[C] :: List[Z] :: HNil

Scala 3 collection partitioning with subtypes(Scala 2/3)

相关问题