您要查找的函数通常称为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
和map
迭代器的实现是完全惰性的。
但为什么停在Iterable
? 这有一个更一般的形式。我们可以为所有可以以这种方式压缩的数据结构声明一个接口。
trait Zip[F[_]] {
def zipWith[A,B,C](f: (A,B) => C, a: F[A], b: F[B]): F[C]
}
例如,我们可以压缩函数:
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库为标准库中的许多数据结构提供了 Applicative 实例。此外,还为ap
. 在 Scalaz 中,这个函数被称为<*>
:
def zipWith[F[_]:Applicative,A,B,C](f: A => B => C, a: F[A], b: F[B]) =
(a map f) <*> b