Scala中有一个等价于Python的更通用的地图功能吗?

时间:2010-04-16 02:17:27

标签: python scala iterable applicative

我知道Scala的列表具有map实现,其签名为(f: (A) => B):List[B]foreach实现了签名(f: (A) => Unit):Unit,但我正在寻找可接受多个迭代的内容与Python map接受多个迭代的方式相同。

我正在寻找签名为(f: (A,B) => C, Iterable[A], Iterable[B] ):Iterable[C]或同等的东西。是否存在存在这样的库或类似的方法?

编辑:

如下所示,我可以做到

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

但是在步骤之间创建了一个临时列表。如果评论员发帖,我可以投票给他(提示,暗示),但还有另一种方式吗?

4 个答案:

答案 0 :(得分:12)

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

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).zip也有一些不足,请确保xs不能是INFINITE Stream。有关详细信息,请转到Ticket #2634。 我前几天在nabble.com上有a post,显示了我对如何修复这张票的看法。

答案 1 :(得分: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))
}

事实证明这种类型更为一般。通常,允许实现此接口的类型构造函数为applicative functors

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)

这概括于任何arity的功能:

  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

答案 2 :(得分:3)

Scala 2.7中的map2对象中有一个方法List(和2.8,但它已被弃用,而不是zipped)。你这样使用它:

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

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

答案 3 :(得分:2)

好吧,我知道语法(f: (A,B) => C, Iterable[A], Iterable[B] ):Iterable[C](而且我知道Scala没有没有),但如果我不得不猜测,那就意味着“函数 f 采用两个可迭代的参数 A B 并返回一个可迭代的 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函数每个人都需要知道,但容易错过:enumeratemapreducezipfilter曾经列在该列表中,但是现在使用列表理解更清晰,更灵活。