scala 输入流
我了解了scala在内部使用Streams记忆的困难方式。
这是我第一次尝试解决欧拉问题5
def from(n: Int): Stream[Int] = n #:: from(n + 1)
def isDivisibleByRange(n: Int, r: Range) = {
r.forall(n % _ == 0)
}
val a = from(21)
val o = a.find(isDivisibleByRange(_, Range(2, 21)))
o match {
case Some(i) => println(i)
case None => println("Nothing found!")
}
我对这个代码为什么会抛出OutOfMemoryError感到有些迷惑,这要归功于Stackoverflow ,因为对这个问题的答案是非常高的232792560,该范围内的所有整数都会在流的不同节点内存储,因此这个问题。
这实际上很容易看到,让我首先修改流生成器函数的副作用:
def from(n: Int): Stream[Int] = {println(s"Gen $n"); n #:: from(n + 1)}
val s = from(1)
s.take(10).toList
s.take(10).toList
第二条语句不会打印任何内容。
鉴于这种记忆行为,有一些可能的修复方法,最简单的方法是在任何地方都不保留对流头的引用,而使用迭代器的find方法:
from(1).iterator.find(isDivisibleByRange(_, Range(1, 21)))
值得一提的是,Java 8流并没有被记住,而使用Java 8流的解决方案(可以大幅度地改进)如下:
@Test
public void testStreamOfInts() {
Stream<Integer> intStream = Stream.generate(from(1));
List<Integer> upto20 = IntStream.rangeClosed(1, 20).boxed().collect(Collectors.toList());
Predicate<Integer> p = (i -> isDivisibleOverRange(i, upto20));
Optional<Integer> o = intStream.filter(p).findFirst();
o.ifPresent(i -> System.out.println("Found: " + i));
}
private Supplier<Integer> from(Integer i) {
AtomicInteger counter = new AtomicInteger(0);
return () -> counter.incrementAndGet();
}
private boolean isDivisibleOverRange(Integer n, List<Integer> l) {
return l.stream().allMatch(i -> n % i == 0);
}
翻译自: https://www.javacodegeeks.com/2014/04/memoization-of-scala-streams.html
scala 输入流