首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >取HList of Seq[_]用笛卡尔乘积生成Seq[HList]

取HList of Seq[_]用笛卡尔乘积生成Seq[HList]
EN

Stack Overflow用户
提问于 2018-04-21 14:38:41
回答 1查看 246关注 0票数 2

我是新来的。

我试图编写一个函数,它将接受不同类型的序列的HList,将其转换为包含原始HList元素的笛卡儿积的Seq[HList],并对结果序列进行迭代。

例如:

代码语言:javascript
复制
val input = Seq(true, false) :: Seq(1,2,3)::Seq("foo", "bar") :: HNil

cartesianProduct: Seq[Boolean :: Int :: String :: HNil] = Seq(
  true :: 1 :: foo :: HNil, 
  true :: 1 :: bar :: HNil, 
  true :: 2 :: foo :: HNil, 
  true :: 2 :: bar :: HNil, 
  true :: 3 :: foo :: HNil, 
  true :: 3 :: bar :: HNil, 
  false :: 1 :: foo :: HNil, 
  false :: 1 :: bar :: HNil, 
  false :: 2 :: foo :: HNil, 
  false :: 2 :: bar :: HNil, 
  false :: 3 :: foo :: HNil, 
  false :: 3 :: bar :: HNil)

我能够在Intellij工作表中使用以下代码实现这一目标:

代码语言:javascript
复制
import shapeless._
import shapeless.ops.hlist.LeftFolder


object combine extends Poly {
  implicit def `case`[T <: HList, S] = use((acc : Seq[T], curr : Seq[S]) => {
    for {
      el <- curr
      v <- acc
    } yield el :: v
  })
}

val input = Seq(true, false) :: Seq(1,2,3)::Seq("foo", "bar") :: HNil

val combinations = input.foldLeft(Seq[HNil](HNil))(combine) 

combinations.foreach(println)

在这里,我想一切都可以工作,因为编译器都知道完整类型的input

但是,当我试图将整个操作包装在一个函数中时,会丢失完整类型的input,并且无法调用foldLeft的结果

代码语言:javascript
复制
def cartesian[T <: HList](input: T)
         (implicit folder: LeftFolder[T, Seq[HNil], combine.type]) = {
  input.foldLeft(Seq[HNil](HNil))(combine)
    .foreach(println)
}

编译器抱怨:

代码语言:javascript
复制
  value foreach is not a member of folder.Out
    input.foldLeft(Seq[HNil](HNil))(combine).foreach(println)
                                            ^

我想有一些隐含的证据,我可以要求断言input (HList of Seq[_])的正确形状,从而让编译器找出最终的foldLeft类型,但我不知道它可能是什么.

希望有人能帮我解决这个问题。谢谢。

更新:这个问题的最终目标是,给定一个HList of Seq[_]来派生一个函数(可能是在一个case类上),该函数将接受一个函数,该函数与输入HList具有相同的arg特性,参数类型与'Seq‘元素类型匹配的顺序相同。例如,对于上面的输入,函数是f: (Boolean, Int, String) => R,所以在f中,我可以使用f迭代输入的笛卡儿积。最终代码如下所示:

代码语言:javascript
复制
import shapeless._
import shapeless.ops.function.FnToProduct
import shapeless.ops.hlist.LeftFolder
import shapeless.syntax.std.function.fnHListOps


object combine extends Poly {
  implicit def `case`[EL <: HList, S] = use((acc : Seq[EL], curr : Seq[S]) => {
    for {
      el <- curr
      v <- acc
    } yield el :: v
  })
}

case class Cartesian[R <: HList, F, FR](combinations: Seq[R])
                                 (implicit ftp: FnToProduct.Aux[F, R => Unit]) {
  def foreach(f: F) =  combinations.foreach(f.toProduct)
}


def cartesian[T <: HList, R <: HList, F, FR](variants: T)(implicit
                             folder: LeftFolder.Aux[T, Seq[HNil], combine.type, _ <: Seq[R]],
                             fnToProd: FnToProduct.Aux[F, R => Unit]
                            ) = {
  val combinations: Seq[R] = variants.foldLeft(Seq[HNil](HNil))(combine)
  Cartesian(combinations)
}

val variants = Seq(true, false) :: Seq("foo", "bar") :: Seq(1, 2, 3) :: HNil

cartesian(variants).foreach((a, b, c) => println(s"$a, $b, $c")) 

注意,函数参数abc的类型是正确推断的,它们是BooleanStringInt。目前,传递给foreach的函数的结果类型必须是固定的(在上面的代码中,它是Unit)。它不能从传入的函数中推断。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-04-21 16:53:50

问题不在于编译器对输入一无所知,而是它对输出一无所知。

def cartesian内部,编译器所知道的就是在foldLeft之后得到某种类型的folder.Out (这取决于一个不具有形状的实例)。

为了确保结果类型,您可以使用LeftFolder.Aux和一个额外的类型参数。

代码语言:javascript
复制
def cartesian[T <: HList](input: T)
         (implicit folder: LeftFolder.Aux[T, Seq[HNil], combine.type, _ <: Seq[Any]]) = {
  input.foldLeft(Seq[HNil](HNil))(combine)
    .foreach(println)
}

现在编译器将知道结果是Seq[Any]的某些子类型,因此可以在其上调用foreach

当然,这只是def内部的问题。在调用站点,输出类型将根据输入进行解析,因此您可以在不使用Aux的情况下完成这一任务。

代码语言:javascript
复制
def cartesian2[T <: HList](input: T)
         (implicit folder: LeftFolder[T, Seq[HNil], combine.type]) = {
  input.foldLeft(Seq[HNil](HNil))(combine)
}

cartesian2(input).foreach(println)

可运行代码:https://scalafiddle.io/sf/n409yNW/2

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49956881

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档