1

在下面的代码中,我试图创建一个 HList 列表,但我在此代码的最后一行遇到编译时错误:

◾找不到参数 tupler 的隐式值:shapeless.ops.hlist.Tupler[shapeless.HList] . 未指定的值参数元组。

object Problem extends App {

  def combinations[T](n: Int, ls: List[T]) = {

    import shapeless._
    import HList._

    def prepareHListR(t: Int, result: HList): HList = t match {
      case t if (t == 0) => result
      case _             => prepareHListR(t - 1, rotateLeft(t - 1, ls) :: result)
    }

    prepareHListR(n, HNil)

  }

  def rotateLeft[A](i: Int, xs: List[A]) = {
    val rot = if (i > 0) i else xs.length + i
    xs.drop(rot) ++ xs.take(rot)
  }

  println(combinations(3, List('a, 'b, 'c, 'd, 'e, 'f)))

}

输出:

列表('a,'b,'c,'d,'e,'f)::列表('b,'c,'d,'e,'f,'a)::列表('c,' d, 'e, 'f, 'a, 'b) :: HNil


接下来我需要做的是创建这些列表的元组,如下所示:

(列表('a,'b,'c,'d,'e,'f),列表('b,'c,'d,'e,'f,'a),列表('c,'d , 'e, 'f, 'a, 'b))

我正在尝试:

combinations(3, List('a, 'b, 'c, 'd, 'e, 'f)).tupled


然而,这种方法在 REPL 上运行良好:

scala> import shapeless._
import shapeless._

scala> import HList._
import HList._

scala> val hlist = List(1, 2, 3) :: List(4, 5, 6) :: List(7, 8, 9) :: HNil
hlist: shapeless.::[List[Int],shapeless.::[List[Int],shapeless.::[List[Int],shapeless.HNil]]] = List(1, 2, 3) :: List(4, 5, 6) :: List(7, 8, 9) :: HNil

scala> val t  =hlist.tupled
t: (List[Int], List[Int], List[Int]) = (List(1, 2, 3),List(4, 5, 6),List(7, 8, 9))


对我来说,这似乎是类型参数的问题,但由于我对 Scala 和 Shapeless 的了解有限,我无法理解它。

任何帮助深表感谢!TIA。

4

1 回答 1

1

这里有几个相关的问题,但重要的一点是元组的大小取决于一个仅在运行时才知道的值(这是一个问题)。该HList版本有效,但只是因为您丢弃了类型。您最终会得到静态类型为 的东西,HList您几乎无法对其执行任何操作(运行时的模式匹配除外),这通常不是您想要的。

你可以编写一个这样的版本,给你一个很好的、静态元组类型的结果,但它需要更多的工作,并且它对你的n论点施加了约束。首先你需要一个自定义类型类:

import shapeless._, ops.tuple.{ Last, Prepend }

trait Rotated[N <: Nat, A] extends DepFn1[List[A]]

object Rotated {
  type Aux[N <: Nat, A, Out0] = Rotated[N, A] { type Out = Out0 }

  implicit def rotated0[A]: Aux[nat._1, A, Tuple1[List[A]]] =
    new Rotated[nat._1, A] {
      type Out = Tuple1[List[A]]

      def apply(l: List[A]): Tuple1[List[A]] = Tuple1(l)
    }

  implicit def rotatedN[N <: Nat, A, OutN, LA](implicit
    rotN: Aux[N, A, OutN],
    last: Last.Aux[OutN, LA],
    ev: LA <:< List[A],
    prep: Prepend[OutN, Tuple1[List[A]]]
  ): Aux[Succ[N], A, prep.Out] =
    new Rotated[Succ[N], A] {
      type Out = prep.Out

      def apply(l: List[A]): prep.Out = {
        val resultN = rotN(l)
        val lastList = ev(last(resultN))
        prep(resultN, Tuple1(lastList.drop(1) ++ lastList.take(1)))
      }
    }
}

有几种方法可以写出来。上面的版本是现成的,但可能是合理的。

现在您的combinations方法如下所示:

def combinations[T, O <: HList](n: Nat, ls: List[T])(implicit
  rotN: Rotated[n.N, T]
): rotN.Out = rotN(ls)

然后用法正是您所要求的:

scala> combinations(3, List('a, 'b, 'c, 'd, 'e, 'f))
res0: (List[Symbol], List[Symbol], List[Symbol]) = (List('a, 'b, 'c, 'd, 'e, 'f),List('b, 'c, 'd, 'e, 'f, 'a),List('c, 'd, 'e, 'f, 'a, 'b))

scala> combinations(2, List('a, 'b, 'c, 'd, 'e, 'f))
res1: (List[Symbol], List[Symbol]) = (List('a, 'b, 'c, 'd, 'e, 'f),List('b, 'c, 'd, 'e, 'f, 'a))

现在你得到了元组,代价是不能做这样的事情:

scala> val x = 4
x: Int = 4

scala> combinations(x, List('a, 'b, 'c, 'd, 'e, 'f))
<console>:20: error: Expression x does not evaluate to a non-negative Int literal
       combinations(x, List('a, 'b, 'c, 'd, 'e, 'f))
                    ^

但是,如果您认为这是一个合理的约束——如果您想要一个大小在编译时可用的元组,则需要以一种在编译时可用的方式指定该大小。

于 2016-11-07T19:53:03.343 回答