我的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);
// Not the primes till 1000, but 1000 first primes
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 解决方案(Streamable
)
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); // sieve method from @frhack answer
primes.forEach(p -> System.out.println(p));
结果:卡在素数之后 =53327
巨大的堆分配和垃圾收集占用超过 90%。从 53323 前进到 53327 需要几分钟,因此等待更多似乎不切实际。
@vidi 解决方案
Prime.stream().forEach(System.out::println);
结果:素数 = 之后的 StackOverflowError 134417
。
我的解决方案(StreamEx)
sieve(StreamEx.iterate(2, x -> x+1)).forEach(System.out::println);
结果:素数 = 之后的 StackOverflowError 236167
。
@frhack 解决方案(rxjava
)
Observable<Integer> primes = Observable.from(()->primesStream.iterator());
primes.forEach((x) -> System.out.println(x.toString()));
结果:素数 = 之后的 StackOverflowError 367663
。
@Holger 解决方案
IntStream primes=from(2).filter(i->p.test(i)).peek(i->p=p.and(v->v%i!=0));
primes.forEach(System.out::println);
结果:素数 = 之后的 StackOverflowError 368089
。
我的解决方案(带有谓词连接的 StreamEx)
sieve(StreamEx.iterate(2, x -> x+1), i -> true).forEach(System.out::println);
结果:素数 = 之后的 StackOverflowError 368287
。
所以三个涉及谓词连接的解决方案获胜,因为每个新条件只增加了 2 个堆栈帧。我认为,它们之间的差异是微不足道的,不应被视为定义赢家。但是我更喜欢我的第一个 StreamEx 解决方案,因为它更类似于 Scala 代码。