あなたが探している関数は通常と呼ばれzipWith
ます。残念ながら、標準ライブラリでは提供されていませんが、次のように書くのは非常に簡単です。
def zipWith[A,B,C](f: (A,B) => C, a: Iterable[A], b: Iterable[B]) =
new Iterable[C] {
def elements = (a.elements zip b.elements) map f.tupled
}
イテレータの実装は完全に怠惰であるためzip
、これは1回だけトラバースします。map
しかし、なぜ立ち止まるのIterable
ですか?これはさらに一般的な形式です。この方法で圧縮できるすべてのデータ構造のインターフェースを宣言できます。
trait Zip[F[_]] {
def zipWith[A,B,C](f: (A,B) => C, a: F[A], b: F[B]): F[C]
}
たとえば、関数をzipできます。
trait Reader[A] {
type Read[B] = (A => B)
}
def readerZip[T] = new Zip[Reader[T]#Read] {
def zipWith[A,B,C](f: (A,B) => C, a: T => A, b: T => B): T => C =
(t: T) => f(a(t),b(t))
}
このタイプのさらに一般的な表現があることが判明しました。一般に、このインターフェースの実装を可能にする型構築子は、適用可能なファンクターです。
trait Applicative[F[_]] {
def pure[A](a: A): F[A]
def map[A,B](f: A => B, a: F[A]): F[B]
def ap[A,B](f: F[A => B], a: F[A]): F[B]
}
zipWithの実装は次のようになります。
def zipWith[F[_],A,B,C](f: A => B => C, a: F[A], b: F[B])
(implicit m: Applicative[F]) =
m.ap(m.map(f,a), b)
これは、あらゆるアリティの機能に一般化されます。
m.ap(m.ap(m.ap(m.map(f,a), b), c), d)
Scalazライブラリは、標準ライブラリの多くのデータ構造に適用可能なインスタンスを提供します。また、便利な構文が。に提供されていap
ます。Scalazでは、この関数は次のように呼ばれ<*>
ます。
def zipWith[F[_]:Applicative,A,B,C](f: A => B => C, a: F[A], b: F[B]) =
(a map f) <*> b