在Scala中,"视图"有什么作用?

del*_*ber 59 scala

具体来说,我在这里看问题1

http://pavelfatin.com/scala-for-project-euler/

列出的代码如下

val r = (1 until 1000).view.filter(n => n % 3 == 0 || n % 5 == 0).sum
Run Code Online (Sandbox Code Playgroud)

我可以遵循除"视图"之外的所有内容.事实上,如果我拿出视图代码仍然编译并产生完全相同的答案.

Kim*_*bel 102

View生成一个惰性集合,因此调用例如filter不评估集合的每个元素.只有在显式访问元素后才会对元素进行评估.现在sum可以访问所有元素,但view调用时filter不会创建完整的Vector.(见史蒂夫的评论)

使用视图的一个很好的例子是:

scala> (1 to 1000000000).filter(_ % 2 == 0).take(10).toList
java.lang.OutOfMemoryError: GC overhead limit exceeded
Run Code Online (Sandbox Code Playgroud)

在这里,Scala尝试使用1000000000元素创建一个集合,然后访问前10个.但是查看:

scala> (1 to 1000000000).view.filter(_ % 2 == 0).take(10).toList
res2: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
Run Code Online (Sandbox Code Playgroud)

  • Nitpick:语句"因为sum访问所有元素,在这里使用视图没有意义"是不正确的,因为`filter`将在调用`sum`之前尝试创建一个完整的`Vector`.尝试在原始示例中使用1000000000替换1000,如果您将"视图"关闭,您将看到上面演示的相同内存问题. (13认同)

Ric*_*lis 25

我对Scala了解不多,但也许这个页面可能有帮助......

实现变换器有两种主要方法.一个是严格的,这是一个新的集合,其所有元素都是由变压器构成的.另一个是非严格或惰性的,即只构造结果集合的代理,并且只在需要时才构造其元素.

一个观点是一种特殊类型的集合,代表一些基本的集合,但懒洋洋地实现了所有变压器.

所以听起来似乎代码仍然可以不用view,但理论上可能会做一些额外的工作,以严格而不是懒惰的方式构建集合的所有元素.


Che*_*ema 9

The following answer is taken from the Views section in the Hands-on Scala book.

When you chain multiple transformations on a collection, we are creating many intermediate collections that are immediately thrown away. For example, in the following code:

@ val myArray = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)
@ val myNewArray = myArray.map(x => x + 1).filter(x => x % 2 == 0).slice(1, 3)

myNewArray: Array[Int] = Array(4, 6)
Run Code Online (Sandbox Code Playgroud)

The chain of .map .filter .slice operations ends up traversing the collection three times, creating three new collections, but only the last collection ends up being stored in myNewArray and the others are discarded.

myArray
1,2,3,4,5,6,7,8,9

map(x => x + 1)
2,3,4,5,6,7,8,9,10

filter(x => x % 2 == 0)
2,4,6,8,10

slice(1, 3)
myNewArray
4,6
Run Code Online (Sandbox Code Playgroud)

This creation and traversal of intermediate collections is wasteful. In cases where you have long chains of collection transformations that are becoming a performance bottleneck, you can use the .view method together with .to to fuse the operations together:

myArray
1,2,3,4,5,6,7,8,9

map(x => x + 1)
2,3,4,5,6,7,8,9,10

filter(x => x % 2 == 0)
2,4,6,8,10

slice(1, 3)
myNewArray
4,6
Run Code Online (Sandbox Code Playgroud)

Using .view before the map/filter/slice transformation operations defers the actual traversal and creation of a new collection until later, when we call .to to convert it back into a concrete collection type:

myArray
1,2,3,4,5,6,7,8,9

view map filter slice to

myNewArray
4,6
Run Code Online (Sandbox Code Playgroud)

This allows us to perform this chain of map/filter/slice transformations with only a single traversal, and only creating a single output collection. This reduces the amount of unnecessary processing and memory allocations.