scala – 从两个HLists创建所有对的HList
我在
Scala中使用无形的,我想编写一个函数allPairs,它将使用两个HLists并返回所有对元素的HList.例如:
import shapeless._ val list1 = 1 :: "one" :: HNil val list2 = 2 :: "two" :: HNil // Has value (1,2) :: (1,"two") :: ("one",2) :: ("one","two") :: HNil val list3 = allPairs(list1,list2) 有什么想法怎么办? 另外,我想强调我正在寻找一个功能,而不是内嵌的代码块. 解决方法
您不能在这里(如其他答案建议)使用一个理解或map和flatMap与函数文字的组合,因为HList上的这些方法需要
higher rank functions.如果只有两个静态类型的列表,这很容易:
import shapeless._ val xs = 1 :: 'b :: 'c' :: HNil val ys = 4.0 :: "e" :: HNil object eachFirst extends Poly1 { implicit def default[A] = at[A] { a => object second extends Poly1 { implicit def default[B] = at[B](a -> _) } ys map second } } val cartesianProductXsYs = xs flatMap eachFirst 这给了我们以下(适当类型): (1,4.0) :: (1,e) :: ('b,4.0) :: ('b,e) :: (c,4.0) :: (c,e) :: HNil 编写一个使用HList参数执行此操作的方法很棘手.以下是一个简单的例子,说明它是如何完成的(有一些比较通用的机器). 我首先要注意的是,我们可以想到找到两个普通列表的笛卡儿积分是“提升”一个函数,它接受两个参数,并将它们作为一个元组返回到应用程序的列表中.例如,你可以写the following in Haskell: import Control.Applicative (liftA2) cartesianProd :: [a] -> [b] -> [(a,b)] cartesianProd = liftA2 (,) 我们可以写一个对应于(,)的多态二进制函数: import shapeless._ object tuple extends Poly2 { implicit def whatever[A,B] = at[A,B] { case (a,b) => (a,b) } } 并再次定义我们的示例列表的完整性: val xs = 1 :: 'b :: 'c' :: HNil val ys = 4.0 :: "e" :: HNil 现在我们将致力于一种名为liftA2的方法,这将使我们能够写下列内容: liftA2(tuple)(xs,ys) 并得到正确的结果.名称liftA2有点误导,因为我们真的没有一个应用函数实例,由于它不是通用的 – 我正在使用名为flatMap的方法和在HList上映射的模型,并且可以接受某些建议更好. 现在我们需要一个类型类,它将允许我们使用Poly2,部分地应用于某个东西,并且通过一个HList映射所得的一元函数: trait ApplyMapper[HF,A,X <: HList,Out <: HList] { def apply(a: A,x: X): Out } object ApplyMapper { implicit def hnil[HF,A] = new ApplyMapper[HF,HNil,HNil] { def apply(a: A,x: HNil) = HNil } implicit def hlist[HF,XH,XT <: HList,OutH,OutT <: HList](implicit pb: Poly.Pullback2Aux[HF,OutH],am: ApplyMapper[HF,XT,OutT] ) = new ApplyMapper[HF,XH :: XT,OutH :: OutT] { def apply(a: A,x: XH :: XT) = pb(a,x.head) :: am(a,x.tail) } } 而现在一个类型的帮助解除的类: trait LiftA2[HF,Y <: HList,Out <: HList] { def apply(x: X,y: Y): Out } object LiftA2 { implicit def hnil[HF,Y <: HList] = new LiftA2[HF,Y,HNil] { def apply(x: HNil,y: Y) = HNil } implicit def hlist[ HF,Out1 <: HList,Out2 <: HList,Out <: HList ](implicit am: ApplyMapper[HF,Out1],lift: LiftA2[HF,Out2],prepend : PrependAux[Out1,Out2,Out] ) = new LiftA2[HF,Out] { def apply(x: XH :: XT,y: Y) = prepend(am(x.head,y),lift(x.tail,y)) } } 最后我们的方法本身: def liftA2[HF,Out <: HList](hf: HF)(x: X,y: Y)(implicit lift: LiftA2[HF,X,Out] ) = lift(x,y) 这就是所有现在的liftA2(元组)(xs,ys)的作品. scala> type Result = | (Int,Double) :: (Int,String) :: | (Symbol,Double) :: (Symbol,String) :: | (Char,Double) :: (Char,String) :: HNil defined type alias Result scala> val res: Result = liftA2(tuple)(xs,ys) res: Result = (1,e) :: HNil 就像我们想要的那样. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |