有很多方法來解決這個問題,並定義自定義類型類(如尼基塔的答案)是一個非常好的答案。不過,我個人發現以下方法更清晰一些。首先讓我們由類羣的異列表爲幺:
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
,雖然你可以很容易地編寫你自己的,它只是一個類型都有證人型類帶有標識元素的附加操作。至關重要的是,對於這個例子,列表(任何東西)都是連接下的monoids,空列表作爲標識元素。
下一頁爲包裝無論你給它在列表中一個簡單的多態函數:
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
這個列表的總和,我們就完成了。
這是所有使用無形狀2.0,但它看起來在1.2中非常相似。
哇,這是一個類型級別的GroupBy。很有意思。將其發佈到無形郵件列表中! – wheaties