You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scala中递归定义的lazy val(Stream[T])如何求值?

Scala递归lazy val与Stream的求值机制解析

核心原因拆解

你的困惑来自对Stream的惰性缓存特性和递归lazy val的初始化逻辑的误解,下面一步步拆解执行过程:

代码执行的详细步骤

当运行incrementStream.take(3).toList时,实际执行流程如下:

  1. 首次调用incrementStream时,lazy val n并未立即初始化(lazy val的特性:首次被引用时才触发初始化)。
  2. 当需要获取第一个元素时,n开始初始化:1 #:: (n map increment1)。这里#::构造的Stream,头部是立即值1,尾部是一个未计算的惰性结构(Stream的map操作本身也是惰性的,不会立即执行)。
  3. 取第一个元素:直接返回1,无需调用increment1。
  4. 取第二个元素:此时需要计算Stream的尾部(即n map increment1的头部)。此时n已经完成初始化,指向同一个Stream实例,所以取n的头部(1)应用increment1,执行一次并打印increment: 1, 2,得到结果2。这个结果会被缓存——既是n map increment1的头部,同时也是n自己的第二个元素(因为n的定义就是1 #:: 尾部,尾部的头部就是n的第二个元素)。
  5. 取第三个元素:需要计算n map increment1的尾部的头部,也就是对n的第二个元素(已缓存的2)应用increment1,执行一次并打印increment: 2, 3,得到结果3。

为什么increment1(1)只执行一次?

你误以为计算n的第二个和第三个元素时都需要用到原始的1,但实际上:

  • Stream的每个元素一旦计算完成就会被缓存,不会重复计算。
  • 递归的lazy val n只会初始化一次,所有对n的引用都是同一个Stream实例。当n的第二个元素被计算为2后,后续依赖n的元素都会直接使用这个缓存的2,而不会再回到原始的1重新计算。

关键特性总结

  • lazy val:仅在首次被引用时初始化,初始化后所有引用指向同一个实例,不会重复初始化。
  • Stream:元素采用惰性计算,每个元素计算完成后会被缓存,后续访问直接使用缓存值,无需重复计算。

内容的提问来源于stack exchange,提问作者utyde

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 06:29:57