Scala 中是不是有与 Python 更通用的 map 函数等价的功能?
Posted
技术标签:
【中文标题】Scala 中是不是有与 Python 更通用的 map 函数等价的功能?【英文标题】:Is there an equivalent in Scala to Python's more general map function?Scala 中是否有与 Python 更通用的 map 函数等价的功能? 【发布时间】:2011-02-08 15:50:03 【问题描述】:我知道 Scala 的 Lists 有一个带有签名 (f: (A) => B):List[B]
的 map 实现和带有签名 (f: (A) => Unit):Unit
的 foreach 实现,但我正在寻找可以像 Python map 一样接受多个迭代的东西接受多个迭代。
我正在寻找带有(f: (A,B) => C, Iterable[A], Iterable[B] ):Iterable[C]
或同等签名的东西。有没有这样的图书馆或类似的类似方式?
编辑:
如下所示,我可以做到
val output = myList zip( otherList ) map( x => x(0) + x(1) )
但这会在步骤之间创建一个临时列表。如果评论者会发帖,我可以给他投票(提示,提示),但还有其他方法吗?
【问题讨论】:
如果zip()
不起作用,您需要采取哪些不同的措施?
一个...拉链的儿子。对!甚至没有想到这一点。你为什么不发帖让我投票。
我会留给@Mike 回答,但你可以回答list1 zip list2
和(list1, list2).zipped
。后者,仅限 Scala 2.8,不会创建临时集合。此外,您可以分别在 Scala 2.8 和 2.7 上执行 list1.view zip list2
或 list1.projection zip list2
以避免创建临时集合。
不,不,请发帖,这样我也可以投票。其他人可能会在以后问同样的问题。真的,zip 并不是我所需要的全部。我需要对两个列表的成员应用一个函数来生成第三个,即最终列表。
我记得最初的问题是询问 Python 中的解决方案。我要疯了吗?
【参考方案1】:
在 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。转至Ticket #2634 了解更多信息。 几天前,我在 nabble.com 上有 a post,它表明了我对如何修复这张票的看法。
【讨论】:
【参考方案2】:您要查找的函数通常称为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))
事实证明,这种类型的表达更为普遍。一般来说,允许实现这个接口的类型构造函数是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)
这可以推广到任何数量的函数:
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
【讨论】:
谢谢。我有很多东西要学。这很有教育意义。【参考方案3】:在 Scala 2.7(和 2.8,但不推荐使用 zipped
)的 List
对象中有一个方法 map2
。你可以这样使用它:
List.map2( List(1,2,3) , List(4,5,6) ) _ * _ // Gives List(4,10,18)
Eastsun 已经展示了如何在 2.8 中使用 zipped
(适用于所有集合,而不仅仅是列表)。
【讨论】:
我很欣赏 2.7 的评论。我很快就会过渡到 2.8。【参考方案4】:好吧,我不知道语法(f: (A,B) => C, Iterable[A], Iterable[B] ):Iterable[C]
(我什么都不知道 Scala),但如果我不得不猜测,它的意思是“A函数 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 函数:enumerate
、map
、reduce
和 zip
。 filter
曾经在该列表中,但现在使用列表理解更清晰、更灵活。
【讨论】:
以上是关于Scala 中是不是有与 Python 更通用的 map 函数等价的功能?的主要内容,如果未能解决你的问题,请参考以下文章
C++ 中是不是有与 python 中的 astype() 函数等效的函数?
C++ 中是不是有与“for ... else”Python 循环等效的方法?
在 C++/STL 中是不是有与 Python range() 等效的紧凑函数