我的StreamEx库现在有了headTail()
操作,可以解决以下问题:
public static StreamEx<Integer> sieve(StreamEx<Integer> input) {
return input.headTail((head, tail) ->
sieve(tail.filter(n -> n % head != 0)).prepend(head));
}
headTail
方法接受一个 BiFunction
,该函数在流终端操作执行期间最多执行一次。因此,这个实现是惰性的:它在遍历开始之前不计算任何内容,并且只计算所请求的质数数量。 BiFunction
接收第一个流元素 head
和其余元素的流 tail
,并且可以以任何希望的方式修改 tail
。您可以将其与预定义输入一起使用:
sieve(IntStreamEx.range(2, 1000).boxed()).forEach(System.out::println);
但是无限流也同样可以工作。
sieve(StreamEx.iterate(2, x -> x+1)).takeWhile(x -> x < 1000)
.forEach(System.out::println);
sieve(StreamEx.iterate(2, x -> x+1)).limit(1000).forEach(System.out::println);
还有一种替代方案,使用 headTail
和谓词连接:
public static StreamEx<Integer> sieve(StreamEx<Integer> input, IntPredicate isPrime) {
return input.headTail((head, tail) -> isPrime.test(head)
? sieve(tail, isPrime.and(n -> n % head != 0)).prepend(head)
: sieve(tail, isPrime));
}
sieve(StreamEx.iterate(2, x -> x+1), i -> true).limit(1000).forEach(System.out::println);
比较递归解法的有趣之处在于:它们能够生成多少个质数。
@John McClean 的解法 (StreamUtils
)
John McClean 的解法不是惰性求值的:你不能用无限流来输入它们。因此,我通过试错找到了最大允许的上限(17793
)(在那之后会出现 StackOverflowError 错误):
public void sieveTest(){
sieve(IntStream.range(2, 17793).boxed()).forEach(System.out::println);
}
@John McClean的解决方案(可流式处理的
)
public void sieveTest2(){
sieve(Streamable.range(2, 39990)).forEach(System.out::println);
}
将上限增加到39990
以上会导致StackOverflowError。
@frhack的解决方案(LazySeq
)
LazySeq<Integer> ints = integers(2)
LazySeq primes = sieve(ints)
primes.forEach(p -> System.out.println(p))
结果:在质数为 53327
时卡住,堆分配和垃圾回收占用超过90%。从53323到53327需要几分钟,因此等待更长时间似乎不切实际。
@vidi的解决方案
Prime.stream().forEach(System.out::println);
结果:质数为134417
后出现了StackOverflowError错误。
我的解决方案(StreamEx)
sieve(StreamEx.iterate(2, x -> x+1)).forEach(System.out::println);
结果:在质数为236167
后发生了StackOverflowError错误。
@frhack的解决方案(rxjava
)
Observable<Integer> primes = Observable.from(()->primesStream.iterator())
primes.forEach((x) -> System.out.println(x.toString()))
结果:质数为367663
后出现了StackOverflowError错误。
@Holger的解决方案
IntStream primes=from(2).filter(i->p.test(i)).peek(i->p=p.and(v->v%i!=0))
primes.forEach(System.out::println)
结果:质数为 368089
时出现堆栈溢出错误。
我的解决方案(使用 StreamEx 和谓词连接)
sieve(StreamEx.iterate(2, x -> x+1), i -> true).forEach(System.out::println)
结果:当质数为368287
时,出现了StackOverflowError错误。
因此,涉及谓词连接的三个解决方案获胜,因为每个新条件只添加了2个堆栈帧。我认为它们之间的差异微不足道,不应该被视为定义获胜者的因素。然而,我更喜欢我的第一个StreamEx解决方案,因为它更类似于Scala代码。
iterator()
。(更不用说你的实现实际上并不是一个适当的质数筛选器;参见例如这篇论文。) - Louis WassermanIntStream.generate(() -> it.next())
,但迭代器的hasNext()
方法会急切地工作并导致无限递归。 - lyomiStream
干净地配合使用。 - Louis Wasserman