问题描述
我有以下 Scala 问题:
I have the following Scala problem:
编写一个接受 HList 列表的函数
Write a function that will take a list of HLists
List(23 :: "a" :: 1.0d :: HNil, 24 :: "b" :: 2.0d :: HNil) # this is list of hlists
并返回列表的HList
and return back HList of Lists
List[Int](23, 24) :: List[String]("a", "b") :: List[Double](1.0d, 2.0d) :: HNil # this is hlist of lists
这有点像通用的 unzipN.任意 HList 是否完全可能?
This is sort of like generic unzipN. Is it at all possible for arbitrary HList?
谢谢.
推荐答案
有很多方法可以解决这个问题,定义自定义类型类(如 Nikita 的回答)是一个非常好的方法.不过,我个人认为以下方法更清晰一些.首先让我们将任何由幺半群组成的异构列表变成一个幺半群:
There are lots of ways to solve this problem, and defining a custom type class (as in Nikita's answer) is a perfectly good one. I personally find the following approach a little clearer, though. First let's make any heterogenous list made up of monoids into a monoid:
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
,尽管你可以很容易地编写自己的——它只是一个类型类,它见证了一个类型具有一个带有标识元素的类似加法的操作.对于这个例子来说,(任何事物的)列表至关重要的是串联起来的幺半群,以空列表作为标识元素.
I'm using Scalaz's Monoid
, although you could pretty easily write your own—it's just a type class that witnesses that a type has a addition-like operation with an identity element. Crucially for this example lists (of anything) are monoids under concatenation, with the empty list as the identity element.
接下来是一个简单的多态函数,它将您提供的任何内容包装在一个列表中:
Next for a simple polymorphic function that wraps whatever you give it in a list:
object singleton extends Poly1 { implicit def anything[A] = at[A](List(_)) }
然后我们将它们联系在一起:
And then we tie it all together:
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
现在我们可以定义我们的例子:
Now we can define our example:
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
使用这种方法,大多数机制都非常通用,并且很容易对每个步骤的作用有直观的了解.考虑以下简化示例:
With this approach most of the machinery is very general, and it's easy to get an intuition for what each step does. Consider the following simplified example:
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
计算列表的总和,我们就完成了.
But we know how to "add" things of type List[Int] :: List[String] :: HNil
, thanks to our monoid machinery at the top. So we can just take the sum of the list using Scalaz's suml
, and we're done.
这一切都使用 Shapeless 2.0,但在 1.2 中看起来非常相似.
This is all using Shapeless 2.0, but it would look pretty similar in 1.2.
这篇关于scala - HList 的通用解压缩的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!