この問題を解決するには多くの方法があり、(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 でもかなり似ています。