我有一个 HList,其中每一列代表表的一列。 HList 中的每个列表的长度相同。
我希望能够编写一个函数,将该表的各个行作为元组或值的 HList 挑选出来。最终我会将其转换为更合理的东西(例如案例类)。
import shapeless.PolyDefns.~>
import shapeless.{HList, HNil}
val a = List(1,2,3) :: List("a", "b", "c") :: List(true, false, true) :: HNil
object broken extends (HList ~> HList) {
def apply[T](n:Int, l:HList): HList = {
// I want to pick out the nth element of each HList
// so in the above example, if n==1
// I want to return
// 2 :: "b" :: false :: HNil
???
}
}
broken(1,a)
我可以修复此功能,使其按照我在评论中描述的内容工作吗?
加分点:我可以把它写成一个迭代器,将上面的 HList "a"转换为 (Int, String, Boolean) 或等效的 HList 的序列吗?
最佳答案
有很多方法可以做到这一点,但我会使用自定义类型类:
import shapeless._
trait RowSelect[L <: HList] extends DepFn2[L, Int] {
type Row <: HList
type Out = Option[Row]
}
object RowSelect {
def select[L <: HList](l: L, i: Int)(implicit rs: RowSelect[L]): rs.Out = rs(l, i)
type Aux[L <: HList, Row0 <: HList] = RowSelect[L] { type Row = Row0 }
implicit val hnilRowSelect: Aux[HNil, HNil] = new RowSelect[HNil] {
type Row = HNil
def apply(l: HNil, i: Int): Option[HNil] = Some(HNil)
}
implicit def hconsRowSelect[A, T <: HList](implicit
trs: RowSelect[T]
): Aux[List[A] :: T, A :: trs.Row] = new RowSelect[List[A] :: T] {
type Row = A :: trs.Row
def apply(l: List[A] :: T, i: Int): Option[A :: trs.Row] = for {
h <- l.head.lift(i)
t <- trs(l.tail, i)
} yield h :: t
}
}
它的工作原理是这样的:
scala> println(RowSelect.select(a, 0))
Some(1 :: a :: true :: HNil)
scala> println(RowSelect.select(a, 1))
Some(2 :: b :: false :: HNil)
scala> println(RowSelect.select(a, 2))
Some(3 :: c :: true :: HNil)
scala> println(RowSelect.select(a, 3))
None
一个
RowSelect
L
的实例见证L
是一个包含所有 List
的 hlist元素,并提供一个操作,可选择从每个 List
中选择指定索引处的项目。 .你应该可以用
NatTRel
完成同样的事情。或 ConstMapper
的组合和 ZipWith
和一个 Poly2
,但是自定义类型类可以很好地将所有内容捆绑在一起,并且在许多情况下允许更方便的组合。例如,在这种情况下,奖金问题的解决方案可以非常简单地写成
RowSelect
:def allRows[L <: HList](l: L)(implicit rs: RowSelect[L]): List[rs.Row] =
Stream.from(0).map(rs(l, _).toList).takeWhile(_.nonEmpty).flatten.toList
进而:
scala> allRows(a).foreach(println)
1 :: a :: true :: HNil
2 :: b :: false :: HNil
3 :: c :: true :: HNil
同样,如果您想将这些 hlist 转换为元组:
def allRows[L <: HList, R <: HList](l: L)(implicit
rs: RowSelect.Aux[L, R],
tp: ops.hlist.Tupler[R]
): List[tp.Out] =
Stream.from(0).map(rs(l, _).map(tp(_)).toList).takeWhile(_.nonEmpty).flatten.toList
这给了你:
scala> allRows(a)
res7: List[(Int, String, Boolean)] = List((1,a,true), (2,b,false), (3,c,true))
等等。
关于scala - 挑选列表列表的第 N 个元素并将该值作为值列表返回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39584034/