使用Java流对任何仅包含lambda的元素执行操作

zdwk9cvp  于 2022-12-02  发布在  Java
关注(0)|答案(2)|浏览(136)

我写这段代码不是为了实用,而是出于好奇......(顺便说一句,我知道标题不太好,但我想不出更好的东西......欢迎提出建议)
请考虑以下事项:

State next = Stream.generate(q::poll).takeWhile(Objects::nonNull)
    .filter(s -> {
        if (atGoal(s)) return true;
        s.expand().forEach(q::add);
        return false;
    }).findFirst().orElse(null);

假设我想缩短它,只使用lambdas...我会怎么做呢?
我设法做到了,但我想知道是否有某种方法可以避免anyMatch(b -> true)部分

State goal = Stream.generate(fringe::poll).takeWhile(Objects::nonNull)
    .filter(s -> atGoal(s) || s.expand().map(fringe::add).anyMatch(b -> true))
    .findFirst().orElse(null);
6yt4nkrj

6yt4nkrj1#

看起来您正在尝试一种遍历算法,如使用Streams的广度优先搜索(代码中的q可能代表Queue)。
在这种情况下,用流替换循环并不是一个好主意,因为您提供的两个版本的代码都已损坏。
这里引用了Stream API文档中的一些内容。

无干扰

因此,其源可能不是并发的流管道中的行为参数不应修改流的数据源。如果行为参数修改流的数据源或导致流的数据源被修改,则行为参数被称为干扰非并发数据源。不干扰的需要适用于所有管道,而不仅仅是并行的。除非源是并发的,否则在流管道执行期间修改流的数据源可能会导致异常、不正确的答案或不一致的行为。
因此,如果流源q不是并发的,代码就被破坏了。
副作用**
如果行为参数确实有副作用,除非明确说明,否则无法保证:

  • 这些副作用对其他线程可见性;
  • 在相同线程中执行对相同流流水线内的“相同”元素的不同操作;和
    *行为参数总是被调用,因为流实现可以自由地从流流水线省略操作(或整个阶段),如果它可以证明它不会影响计算的结果。

引用Stream.filter()的Javadoc

参数:

predicate-非干扰无状态 predicate ,应用于每个元素以确定是否应包括该元素
因此,您使用filterPredicate来执行副作用并干扰数据源。文档中不鼓励这样做。API中没有为此目的而设计的中间操作。中间操作的本质是惰性的,在某些情况下可以优化掉。因此,你真正需要执行的效果不应该包含在中间操作中。对于另一个答案中提到的peek()尤其如此,根据文档,它被专门设计为支持调试,并且可以从管道中丢弃,因为它不是“t意味着执行结果操作,而不是终端操作forEach()forEachOrdered(),它们也应该谨慎使用(它们对实现BFS没有帮助)。
也就是说,没有正确和干净的方法来实现您想要使用流。

4jb9z9bj

4jb9z9bj2#

最近的问题似乎是,如何确定s.expand()是否产生空流t。

  • t.findAny()
  • t.reduce(false, (x,y) -> true)
  • t.collect(Collectors.toList()).isEmpty()
  • t.iterator().hasNext()

但是我看不出有什么方法可以像s.expand()中那样从流开始,并且在不进行任何进一步计算的情况下捕获它是否为空或不是布尔形式的事实。
你所能做的就是在你的方法中移动功能,比如

  • s.expand()作为参数,如int addSome(Stream<whatever> s)...,得到.filter(s -> atGoal(s) || fringe.addSome(s.expand()) > 0,但它与@cghislai的建议一致,可能不符合您对lambda唯一性的想法。
  • 边缘元素知道它们所在的集合,如果它们是atGoal,则给出一些.filter(s -> s.atGoalOrCouldExpand())
  • 使用方便的方法实现流装饰器

顺便说一句,也许仅仅是偷看就可以更好地强调你正在产生副作用的事实:.peek(fringe::add)并留下初始流(和相同的问题)。

相关问题