本文介绍了挑出列表 HList 的第 N 个元素并将该值作为 HList 值返回的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 HList,其中每一列代表表的一列.HList 中的每个列表的长度相同.

I have an HList in which each column represents a column of a table. Each list in the HList is of the same length.

我希望能够编写一个函数,将该表的各个行作为元组或值的 HList 挑选出来.最终,我会将其转换为更合理的内容(例如案例类).

I'd like to be able to write a function which picks out individual rows of this table as a tuple or an HList of values. Eventually I will convert this to something a bit more sensible (e.g. a Case Class).

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)

我可以修复这个功能,让它按照我在评论中描述的方式工作吗?

Can I fix this function so that it works according to what I've described in the comments?

加分项:我可以把它写成一个迭代器,将上面的 HList "a" 转换成一个 (Int, String, Boolean) 或等效的 HList 序列吗?

Bonus points: Can I write this as an iterator that transforms my HList "a" above into a sequence of (Int, String, Boolean) or an equivalent HList?

推荐答案

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

There are a number of ways you could do this, but I'd go with a custom type class:

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 中选择指定索引处的项目的操作.

A RowSelect instance for L witnesses that L is an hlist with all List elements, and provides an operation that optionally selects the item at a specified index from each List.

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

You should be able to accomplish the same thing with NatTRel or a combination of ConstMapper and ZipWith and a Poly2, but a custom type class bundles everything together nicely and in many cases allows more convenient compositionality.

例如,在这种情况下,您的奖金问题的解决方案可以非常简单地根据 RowSelect 编写:

For example, in this case the solution to your bonus question can be pretty straightforwardly written in terms of 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 转换为元组:

And similarly if you want to convert these hlists to tuples:

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

等等.

这篇关于挑出列表 HList 的第 N 个元素并将该值作为 HList 值返回的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-26 23:07