【发布时间】:2018-11-19 03:33:30
【问题描述】:
为了好玩,我正在制作我自己的 Java 流库版本。这是我的班级签名:
class Stream<T> {
Supplier<T> head;
Supplier<Stream<T>> tail;
...
}
另外,我写了一个基本的无限流迭代器,它会根据给定的函数生成一个无限列表:
public static <T> Stream<T> iterate(T first, Function<T, T> f) {
return new Stream<T>(
() -> first,
() -> {
T nextElem = f.apply(first);
if (nextElem == null) {
return generate(() -> null);
} else {
return iterate(nextElem, f);
}
}
);
}
函数generate 是迭代的一个特例,它永远重复给定元素。在上面的函数中,我生成了一个无限序列null 来指示流的结束(我认为我不会在流中存储空值)。
然后我写了一个 reduce 函数,它的第二个参数是惰性的:
public <U> U reduce(U acc, Function<T, Function<Supplier<U>, U>> f) {
System.out.println("REDUCE CALL");
T elem = head.get();
if (elem != null) {
return f.apply(elem).apply(() -> this.tail.get().reduce(acc, f));
} else {
return acc;
}
}
在reduce函数的基础上,我编写了filter函数。
public Stream<T> filter(Predicate<T> p) {
System.out.println("FILTER");
return reduce(generate(() -> null), elem -> acc -> {
if (p.test(elem)) {
return new Stream<>(
() -> elem,
() -> acc.get()
);
} else {
return acc.get();
}
});
}
最后,我开始使用我自己的 Stream 类:
public static void main(String[] args) {
Stream<Integer> ilist =
Stream
.iterate(1, x -> x + 1)
.filter(x -> x >= 5);
}
但过滤器并不懒惰!从下面给出的输出中,我认为过滤器会评估元素,直到找到与给定谓词匹配的元素。
FILTER
REDUCE CALL
REDUCE CALL
REDUCE CALL
REDUCE CALL
REDUCE CALL
我的代码出了什么问题,我怎样才能让我的过滤器函数再次变得惰性?
更新:根据 Sweeper 的说法,我在不使用 reduce 的情况下又尝试了 filter 功能。
public Stream<T> filter2(Predicate<T> p) {
System.out.println("FILTER2");
T elem = head.get();
if (elem == null) {
return generate(() -> null);
} else {
if (p.test(elem)) {
return new Stream<>(
() -> elem,
() -> this.tail.get().filter2(p)
);
} else {
return this.tail.get().filter2(p);
}
}
}
然而,这个函数也不是惰性的。我使用filter2的main函数的输出如下:
FILTER2
FILTER2
FILTER2
FILTER2
FILTER2
我该如何解决这个问题,有没有办法通过惰性减少来实现惰性过滤器?
致谢:本练习和上述函数的实现受到 Chiusano 和 Bjarnason 的Scala 中的函数式编程一书的启发。
【问题讨论】:
-
filter并不懒惰,因为reduce并不懒惰。我不认为你可以用非懒惰的reduce来实现懒惰的filter。 -
@Sweeper 有没有办法让 reduce 变得懒惰?
-
完全不使用reduce会怎样?
-
@Sweeper 我试过了。请参阅帖子以获取更新。
标签: java lambda java-8 functional-programming lazy-evaluation