9

我知道 Scala 的 Lists 有一个带有签名的map实现(f: (A) => B):List[B]和一个带有签名的foreach实现,(f: (A) => Unit):Unit但我正在寻找接受多个 iterables 的东西,就像 Python map接受多个 iterables 一样。

我正在寻找带有(f: (A,B) => C, Iterable[A], Iterable[B] ):Iterable[C]或等效签名的东西。有没有这样的图书馆或类似的类似方式?

编辑:

如下所示,我可以做

val output = myList zip( otherList ) map( x => x(0) + x(1) )

但这会在步骤之间创建一个临时列表。如果评论者会发帖,我可以给他投票(提示,提示),但还有其他方法吗?

4

4 回答 4

12

在 scala 2.8 中,在 Tuple2 和 Tuple3 中有一个名为 zipped 的方法,可以避免创建临时集合。这是一些示例用例:

Welcome to Scala version 2.8.0.r21561-b20100414020114 (Java HotSpot(TM) Client VM, Java 1.6.0_18).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val xs = 0 to 9
xs: scala.collection.immutable.Range.Inclusive with scala.collection.immutable.Range.ByOne = Range(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> val ys = List.range(0,10)
ys: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> val zs = Array.range(0,10)
zs: Array[Int] = Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> (xs,ys).zipped.map{ _+_ }
res1: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 2, 4, 6, 8, 10, 12, 14, 16, 18)

scala> (zs,ys,xs).zipped.map{ _+_+_ }
res2: Array[Int] = Array(0, 3, 6, 9, 12, 15, 18, 21, 24, 27)

scala>

Tuple2 和 Tuple3 中都有一个 zip 方法。xs.zip(ys) 与 (xs,ys).zip 相同

注意: (xs,ys).zip 和 (xs,ys).zipped 中也有一些不足,请确保 xs 不能是 INFINITE Stream。获取更多信息,请访问票证 #2634。几天前,我在 nabble.com 上发表了一篇文章,显示了我对如何修复这张票的看法

于 2010-04-16T07:07:49.763 回答
11

您要查找的函数通常称为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
  }

这只会遍历一次,因为迭代器zipmap迭代器的实现是完全惰性的。

但为什么停在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
于 2010-04-16T13:29:47.073 回答
3

Scala 2.7(和 2.8,但不推荐使用它以支持)map2中的对象中有一个方法。你像这样使用它:Listzipped

List.map2( List(1,2,3) , List(4,5,6) ) { _ * _ }  // Gives List(4,10,18)

Eastsun 已经展示了如何zipped在 2.8 中使用(适用于所有集合,而不仅仅是列表)。

于 2010-04-16T12:12:58.923 回答
2

好吧,我不知道语法(f: (A,B) => C, Iterable[A], Iterable[B] ):Iterable[C](我对 Scala也一无所知),但如果我不得不猜测,它的意思是“一个函数f接受两个可迭代的参数AB并返回一个可迭代的C ”。我不确定这是否意味着所有可迭代对象都产生相同数量的项目。

在 Python 中,我认为您正在寻找zip函数:

>>> A = range(10, 15)
>>> B = range(1000, 1500, 100)
>>> zip(A, B)
[(10, 1000), (11, 1100), (12, 1200), (13, 1300), (14, 1400)]
>>> [a + b for a,b in zip(A, B)]
[1010, 1111, 1212, 1313, 1414]

zip的输出只要最短的可迭代:

>>> A=range(10, 12)
>>> zip(A, B)
[(10, 1000), (11, 1100)]

无论如何,一些每个人都需要知道但很容易错过的内置 Python 函数:enumeratemapreducezip. filter曾经在该列表中,但现在使用列表理解更清晰、更灵活。

于 2010-04-16T12:17:14.043 回答