为何Kotlin Sequence变量捕获会导致执行结果出现差异?
Kotlin Sequence中Lambda捕获导致素数生成结果异常的原因
在素数生成的代码中,将prime变量从while循环内部移到外部声明后,Sequence的输出结果完全不符合预期。这背后的核心原因是Lambda对变量的捕获机制以及Sequence的惰性求值特性共同作用导致的。
正常代码(正确生成素数)
val primes: Sequence<Int> = sequence { var numbers = generateSequence(2) { it + 1 } while (true) { val prime = numbers.first() yield(prime) numbers = numbers.drop(1) .filter { it % prime != 0 } } } println(primes.take(10).toList()) // [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
问题代码(生成结果不符合预期)
val primes: Sequence<Int> = sequence { var numbers = generateSequence(2) { it + 1 } var prime: Int while (true) { prime = numbers.first() yield(prime) numbers = numbers.drop(1) .filter { it % prime != 0 } } } println(primes.take(10).toList()) // [2, 3, 5, 6, 7, 8, 9, 10, 11, 12]
关键差异分析
- 变量捕获的本质不同
- 正常代码中,
prime是while循环内的val变量:每次循环都会创建一个全新的不可变变量,filter中的Lambda捕获的是当前循环这个prime的具体值。由于val不可变,即便Sequence是惰性求值,后续filter执行时引用的还是当初捕获的那个素数(比如第一次循环的2、第二次的3),能正确过滤非素数。 - 问题代码中,
prime是while循环外的var变量:Lambda捕获的是这个变量的引用而非当前值。当后续Sequence迭代触发filter逻辑时,prime已经被后续循环重新赋值为新的数值(比如第一次循环后变成3、第二次变成5),导致filter判断时用的是最新的prime值,而非创建filter时对应的素数,过滤逻辑完全失效。
- 正常代码中,
- Sequence的惰性求值放大了问题
Kotlin Sequence的中间操作(如filter)不会立即处理元素,只有终端操作(如take(10).toList())触发时才会逐个迭代执行逻辑。这让问题代码中Lambda的执行时机被延迟,正好赶上prime变量被多次修改,最终导致结果异常。
内容的提问来源于stack exchange,提问作者yellowsymphony
相关产品推荐
相关产品推荐

