【问题标题】:Pick out the Nth element of a HList of Lists and return that value as a HList of values挑选 List of Lists 的第 N 个元素并将该值作为 List of values 返回
【发布时间】:2016-09-20 21:07:12
【问题描述】:

我有一个 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)

我能否修复此功能,使其按照我在 cmets 中的描述工作?

加分项:我可以将其编写为一个迭代器,将上面的 HList“a”转换为 (Int, String, Boolean) 序列或等效的 HList?

【问题讨论】:

    标签: scala shapeless


    【解决方案1】:

    有很多方法可以做到这一点,但我会使用自定义类型类:

    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
    

    LRowSelect 实例证明 L 是一个包含所有 List 元素的 hlist,并提供了一个操作,可以选择从每个 List 中选择指定索引处的项目。

    您应该能够使用NatTRelConstMapperZipWithPoly2 的组合来完成同样的事情,但是自定义类型类将所有内容很好地捆绑在一起,并且在许多情况下允许更方便的组合性.

    例如,在这种情况下,您的奖金问题的解决方案可以非常直接地写成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))
    

    等等。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-12-01
    • 2021-06-22
    • 1970-01-01
    • 1970-01-01
    • 2012-11-29
    • 2022-12-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多