Kotlin实现埃氏筛问题:为何输出缺失4且混入非质数?
问题本质:闭包捕获引用而非值 + 序列懒加载的执行时机
你的错误代码中,所有filter操作里的lambda捕获的是同一个可变prime变量的引用,而非每次添加filter时prime的具体值。因为序列是懒加载的,所有filter的条件判断都要等到实际遍历序列时才执行,而此时循环已经更新了prime变量的值,导致所有filter都使用了当前(或最后一次循环)的prime值,而非各自对应的质数。
为什么会出现「缺失4,混入6、8」的现象?
举个对应错误逻辑的场景:
- 初始序列为
2,3,4,5,6,7,8,... - 第一次循环取
prime=2,添加filter「it%prime !=0」,然后返回2 - 第二次循环取新序列的第一个元素(此时filter还未执行)得到3,更新
prime=3,添加新的filter「it%prime !=0」,然后返回3 - 当你继续遍历取后续元素时,才会执行之前添加的所有filter:
- 第一个filter的条件实际是「
it%3 !=0」(因为prime已经被更新为3),而非你预期的「it%2 !=0」 - 这就导致2的倍数(如6、8)不会被过滤,只要它们不是当前prime的倍数就能通过;而4这类数,可能在遍历到它时,prime已经更新为更大的值(比如3),4%3≠0本应通过,但如果你的代码逻辑中在第二次循环时直接替换了序列源,可能导致4被提前跳过,最终出现缺失。
- 第一个filter的条件实际是「
修复方案
在每次添加filter时,把当前的prime值快照到一个局部val变量中,让filter捕获这个固定值(而非外部可变变量的引用):
fun sieveCorrect(): Sequence<Int> { var sequence = generateSequence(2) { it + 1 } return sequence { while (true) { val prime = sequence.first() // 快照当前prime值到局部变量,确保filter捕获固定值 val currentPrime = prime sequence = sequence.filter { it % currentPrime != 0 } yield(prime) } } }
你的理解误区总结
你错误地假设了两种极端情况:要么所有filter共享同一个prime值,要么每个filter都严格使用创建时的prime值。但实际是:
- 闭包捕获的是变量引用而非值,序列懒加载延迟了filter的执行时机,导致filter判断时使用的是prime变量的最新值,而非创建filter时的初始值
- 这种混合状态就造成了既没正确过滤早期质数的倍数(如6、8),又意外跳过或保留了某些数(如4)的异常结果
内容的提问来源于stack exchange,提问作者JSW
相关产品推荐
相关产品推荐

