我有以下Scala问题:
编写一个将采用HLists列表的函数
List(23 :: “a” :: 1.0d :: HNil, 24 :: “b” :: 2.0d :: HNil) # this is list of hlists
并返回列表的HList
List[Int](23, 24) :: List[String](“a”, “b") :: List[Double](1.0d, 2.0d) :: HNil # this is hlist of lists
这有点像通用的unzipN。任意HList都可能吗?
谢谢。
答案 0 :(得分:6)
有很多方法可以解决这个问题,定义自定义类型(如Nikita的答案)是一个非常好的方法。不过,我个人觉得以下方法更清楚一些。首先让我们将由幺半群组成的异构列表变成一个幺半群:
import shapeless._
import scalaz._, Scalaz._
implicit object hnilMonoid extends Monoid[HNil] {
val zero = HNil
def append(f1: HNil, f2: => HNil) = HNil
}
implicit def hconsMonoid[H: Monoid, T <: HList: Monoid] = new Monoid[H :: T] {
val zero = Monoid[H].zero :: Monoid[T].zero
def append(f1: H :: T, f2: => H :: T) =
(f1.head |+| f2.head) :: (f1.tail |+| f2.tail)
}
我正在使用Scalaz的Monoid
,虽然您可以很容易地编写自己的 - 它只是一个类型类,它见证了类型具有与标识元素类似的加法操作。至关重要的是,这个例子列表(任何东西)都是连接下的幺半群,空列表作为标识元素。
接下来是一个简单的多态函数,它包含你在列表中给出的任何内容:
object singleton extends Poly1 { implicit def anything[A] = at[A](List(_)) }
然后我们将它们捆绑在一起:
def unzipN[L <: HList, Out <: HList](hlists: List[L])(implicit
mapper: ops.hlist.Mapper.Aux[singleton.type, L, Out],
monoid: Monoid[Out]
): Out = hlists.map(_ map singleton).suml
现在我们可以定义我们的示例:
val myList = List(23 :: "a" :: 1.0d :: HNil, 24 :: "b" :: 2.0d :: HNil)
我们已经完成了:
scala> println(unzipN(myList))
List(23, 24) :: List(a, b) :: List(1.0, 2.0) :: HNil
通过这种方法,大多数机器都非常通用,并且很容易对每个步骤的作用有所了解。请考虑以下简化示例:
val simple = List(1 :: "a" :: HNil, 2 :: "b" :: HNil)
现在simple.map(_ map singleton)
就是以下内容:
List(List(1) :: List("a") :: HNil, List(2) :: List("b") :: HNil)
但是我们知道如何“添加”List[Int] :: List[String] :: HNil
类型的东西,这要归功于我们顶级的幺半群机器。所以我们可以使用Scalaz的suml
来获取列表的总和,我们已经完成了。
这都是使用Shapeless 2.0,但它在1.2中看起来非常相似。
答案 1 :(得分:3)
要解决此问题,您需要一个自定义类型类:
import shapeless._
trait Unzipper[ -input ]{
type Output
def unzip( input: input ): Output
}
object Unzipper {
implicit def headTail
[ head, tail <: HList ]
( implicit tailUnzipper: Unzipper[ List[ tail ] ]{ type Output <: HList } )
=
new Unzipper[ List[ head :: tail ] ]{
type Output = List[ head ] :: tailUnzipper.Output
def unzip( list: List[ head :: tail ] ) =
list.map(_.head) :: tailUnzipper.unzip(list.map(_.tail))
}
implicit val nil =
new Unzipper[ List[ HNil ] ]{
type Output = HNil
def unzip( list: List[ HNil ] ) = HNil
}
}
val list = List(23 :: "a" :: 1.0d :: HNil, 24 :: "b" :: 2.0d :: HNil)
println( implicitly[Unzipper[list.type]].unzip(list) )
输出:
List(23, 24) :: List(a, b) :: List(1.0, 2.0) :: HNil