懒惰计算步骤:筛选列表

10 浏览
0 Comments

懒惰计算步骤:筛选列表

为什么在找到"Jill"之后,评估必须重新开始(从"Mark"开始)?为什么它没有继续评估直到列表的末尾?

0
0 Comments

原因:在原始的代码中,对列表进行筛选时,每次使用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],它总是返回一个新的集合。

0
0 Comments

问题的原因是在实现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将只计算一次,从而提高了性能。

0