首页 > 解决方案 > 我可以使用函数组合来避免scala中的“临时列表”吗?

问题描述

在 fpis《function programming in scala》的第 64 页上说

List(1,2,3,4).map(_ + 10).filter(_ % 2 == 0).map(_ * 3)

“每个转换都会产生一个临时列表,该列表只会被用作下一个转换的输入,然后立即被丢弃”

所以编译器或库不能帮助避免这种情况?

如果是这样,这个haskell代码是否也会产生一个临时列表?

map (*2) (map (+1) [1,2,3])

如果是,我可以使用函数组合来避免这种情况吗?

map ((*2).(+1)) [1,2,3]

如果我可以使用函数组合来避免haskell中的临时列表,我可以使用函数组合来避免scala中的临时列表吗?

我知道 scala 使用功能“撰写”来撰写功能:https ://www.geeksforgeeks.org/scala-function-composition/

那么我可以写这个来避免scala中的临时列表吗?

((map(x:Int=>x+10)) compose (filter(x=>x%2==0)) compose (map(x=>x*3)) (List(1,2,3,4))

(IDEA告诉我我不能)

谢谢!

标签: scala

解决方案


编译器不应该这样做。如果您考虑地图融合,它可以很好地与纯函数一起使用:

List(1, 2, 3).map(_ + 1).map(_ * 10)
// can be fused to
List(1, 2, 3).map(x => (x + 1) * 10)

然而,Scala 不是一种纯粹的函数式语言,它也没有任何编译器可以跟踪的纯度概念。例如,由于副作用,行为会有所不同:

List(1, 2, 3).map { i => println(i); i + 1 }.map { i => println(i); i * 10 }
// prints 1, 2, 3, 2, 3, 4

List(1, 2, 3).map { i =>
  println(i)
  val j = i + 1
  println(j)
  j * 10
}
// prints 1, 2, 2, 3, 3, 4

另一件需要注意的是,ScalaList是一个严格的集合——如果你有一个对列表的引用,那么它的所有元素都已经分配在内存中。相反,Haskell 列表是惰性的(就像 Haskell 中的大多数东西一样),所以即使创建了临时的“列表外壳”,它的元素在需要之前都不会被计算。这也允许 Haskell 列表是无限的(你可以写成[1..]越来越多的数字)

与 Haskell list 最接近的 Scala 对应项是LazyList,它在请求之前不会评估其元素,然后缓存它们。这样做

LazyList(1,2,3,4).map(_ + 10).filter(_ % 2 == 0).map(_ * 3)

将分配中间LazyList实例,但在从最终列表中请求它们之前不计算/分配其中的任何元素。LazyList也适用于无限集合(LazyList.from(1)类似于上面的 Haskell 示例,除了它是 Int)。

实际上,在这里,map两次处理副作用或手动融合都没有区别。

您可以通过做将任何集合切换为“惰性” .view,或者通过做只使用迭代器.iterator- 它们与任何集合具有大致相同的API,然后通过做返回到具体集合.to(Collection),例如:

List(1,2,3,4).view.map(_ + 10).filter(_ % 2 == 0).map(_ * 3).to(List)

将在没有任何中介的情况下制作清单。问题是它不一定更快(尽管通常内存效率更高)。


推荐阅读