List.view和LazyList之间有什么区别?

9
我刚学习Scala,了解到LazyList被创建用来替代Stream,同时他们给所有集合添加了.view方法。所以我想知道为什么在Scala集合库中新增了LazyList,当我们可以使用List.view时呢?我查看了Scaladoc,似乎唯一的区别是LazyList具有记忆功能,而View没有。我理解的对吗?

很遗憾,我觉得这里的回答都不是很完整。我仍然不明白拥有两者的好处。现在视图似乎相当无用,因为它们缺少许多方法,但我可能漏掉了什么... - Dici
这里有一些提示,但仍然不够全面:https://www.baeldung.com/scala/stream-vs-views-vs-iterators - Dici
@Dici,使用LazyList的好处在于它的记忆化。而List.view则不提供记忆化功能。有时候记忆化并不是必要的,因为它会占用内存,这时你可以使用LazyList。 - pavel_orekhov
我之前提出了一个问题,后来得到了有用的答案:https://dev59.com/4MLra4cB1Zd3GeqPQ8Sh。我仍然认为只使用迭代器和惰性列表就可以完成大多数任务,但是对于某些用例,视图确实很有用。不确定这是否值得增加语言的复杂性,但是好吧。 - Dici
3个回答

8

Stream元素是惰性地实现的,除了第一个(头)元素。这被认为是一种不足之处。

List视图是惰性重新评估的,但据我所知,必须首先完全实现。

def bang :Int = {print("BANG! ");1}

LazyList.fill(4)(bang)  //res0: LazyList[Int] = LazyList(<not computed>)
Stream.fill(3)(bang)    //BANG! res1: Stream[Int] = Stream(1, <not computed>)
List.fill(2)(bang).view //BANG! BANG! res2: SeqView[Int] = SeqView(<not computed>)

这很有道理,但我是从已经拥有视图和列表的角度来谈论的,而不是从获得它们之前发生的情况的角度来谈论,但是你的回答很好!我也没有想过这一点。 - pavel_orekhov
嘿,你!这很有趣。通常人们关心每一点懒惰,正如jwh所描述的那样。我猜“重新评估”意味着像map等转换。 - som-snytt
只是想知道,因为我是Scala的新手,我的“go to” Seq集合应该是什么?Vector?@som-snytt关于强制的好观点! - pavel_orekhov

5
在2.13版本中,您无法强制从视图返回到原始集合类型:
scala> case class C(n: Int) { def bump = new C(n+1).tap(i => println(s"bump to $i")) }
defined class C

scala> List(C(42)).map(_.bump)
bump to C(43)
res0: List[C] = List(C(43))

scala> List(C(42)).view.map(_.bump)
res1: scala.collection.SeqView[C] = SeqView(<not computed>)

scala> .force
            ^
       warning: method force in trait View is deprecated (since 2.13.0): Views no longer know about their underlying collection type; .force always returns an IndexedSeq
bump to C(43)
res2: scala.collection.IndexedSeq[C] = Vector(C(43))

scala> LazyList(C(42)).map(_.bump)
res3: scala.collection.immutable.LazyList[C] = LazyList(<not computed>)

scala> .force
bump to C(43)
res4: res3.type = LazyList(C(43))

如果一个函数需要接收一个视图,并可选地返回一个严格的实例,那么如果调用者需要选择结果类型,则还必须接收一个“强制函数”,如_.toList

我在我的日常工作中不会做这种事情,但这种行为让我感到惊讶。


1
LazyList 的区别在于它可以从巨大/无限的序列中生成,所以可以进行如下操作:
val xs = (1 to 1_000_000_000).to(LazyList)

这样就不会耗尽内存。之后,您可以使用转换器对“惰性列表”进行操作。如果创建一个List并从中获取view,则无法执行相同的操作。话虽如此,SeqView的方法集比LazyList更丰富,这就是为什么您实际上可以像这样获取LazyListview

val xs = (1 to 1_000_000_000).to(LazyList)
val listView = xs.view

但是,由于记忆化,当我迭代所有惰性列表的元素时,它会耗尽内存吗? - pavel_orekhov
1
@pavel_orekhov 是的,但如果您需要对其应用多个过滤器和转换,然后仅访问一小部分元素-那么您是很好的。 - Telek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接