如何在HLists上实现zipWithIndex

4
写算法时,我需要一个 zipWithIndex 函数来操作 HList。目前 shapeless 库中还没有这个函数,所以我决定自己实现它。
很明显,它可以像这样实现:
hlist.zip(indexes)

其中indexes是索引的HList(0..n),可能可以通过以下方式获得:

val indexes = Nat._0 until hlist.length

问题在于Nat类没有until方法。我没有找到任何Witness来用于HList.map和HList索引。有什么方法可以获取从Nat._0开始到hlist.length的Nat类型的HList吗?
1个回答

6
也许将类似这样的内容添加到Shapeless中是有意义的:
import shapeless._, ops.hlist.Prepend

trait Range[A <: Nat, B <: Nat] extends DepFn0 { type Out <: HList }

object Range {
  type Aux[A <: Nat, B <: Nat, Out0 <: HList] = Range[A, B] { type Out = Out0 }

  implicit def emptyRange[A <: Nat]: Aux[A, A, HNil] = new Range[A, A] {
    type Out = HNil
    def apply(): Out = HNil
  }

  implicit def slightlyBiggerRange[A <: Nat, B <: Nat, OutAB <: HList](implicit
    rangeAB: Aux[A, B, OutAB],
    appender: Prepend[OutAB, B :: HNil],
    witnessB: Witness.Aux[B]
  ): Aux[A, Succ[B], appender.Out] = new Range[A, Succ[B]] {
    type Out = appender.Out
    def apply(): Out = appender(rangeAB(), witnessB.value :: HNil)
  }
}

def range[A <: Nat, B <: Nat](implicit r: Range[A, B]): r.Out = r()

现在你可以相当简洁地编写zipWithIndex
import ops.hlist.{ Length, Zip }

def zipWithIndex[L <: HList, S <: Nat, R <: HList, Out <: HList](l: L)(implicit
  len: Length.Aux[L, S],
  range: Range.Aux[nat._0, S, R],
  zipper: Zip.Aux[L :: R :: HNil, Out]
): Out = l.zip(range())

然后:
import nat._

type Expected = (Int, _0) :: (Symbol, _1) :: (String, _2) :: HNil

val xs: Expected = zipWithIndex(1 :: 'a :: "foo" :: HNil)

你也可以使用一个 fold 或者 ZippedWithIndex[L <: HList] 类型类,它们可能会更加简洁,但是不太清晰地由独立有用的部分组成,例如 Range


确实可能是 :-) 对于Coproducts和元组也是这样吗? - Miles Sabin
作为一个Shapeless初学者,我并不完全理解它。我也投票将其包含在Shapeless发行版中。 - david.perez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接