懒惰计算步骤:筛选列表
原因:在原始的代码中,对列表进行筛选时,每次使用filter
方法都会返回一个新的集合,这导致在找到"Jill"之后,评估必须从头开始重新进行,重新筛选列表。
解决方法:使用流(Stream)可以避免这个问题。流是一个延迟列表,Scala中的流实际上是一个列表,其尾部是一个惰性值(lazy val)。一旦计算完成,一个值就会一直保持计算状态并被重复使用,也就是说,这些值被缓存起来。通过使用流,可以将筛选操作合并为一个操作,避免重复计算和重新评估。
修改后的代码如下:
println(people.toStream .filter(p => isOlderThan17(p)) .filter(p => nameStartsWithJ(p)) .last)
如果只想要找到最后一个元素,流并不是非常内存高效的选择。在这种情况下,可以将两个谓词合并,并且只进行一次筛选操作。
修改后的代码如下:
println(people.filter(p => isOlderThan17(p) && nameStartsWithJ(p)) .last)
至于第三个问题,是否会将视图(View)中的筛选方法合并为一个筛选方法,我认为在这个例子中,每个筛选都会生成一个新的集合。因为filter
方法的签名是filter(p: (A) ⇒ Boolean): List[A]
,它总是返回一个新的集合。
问题的原因是在实现view
中的last
方法时,last
方法尝试两次访问head
,这意味着需要两次查找people.view.filter(isOlderThan17(_))
的第一个元素,因此view
必须重新计算两次。
要解决这个问题,可以将people.view.filter(isOlderThan17(_))
的结果缓存起来以避免重复计算。可以使用view.force
方法将view
转换为普通的集合,并将其存储在一个变量中,然后在后续的操作中使用该变量。这样,每次访问view
时都不会重新计算。
下面是修改后的代码:
val filteredList = people.view.filter(isOlderThan17(_)).force filteredList.filter(nameStartsWithJ(_)).fold(None)((x, y) => Some(y))
这样一来,filteredList
将只计算一次,从而提高了性能。