Functional programming 使用索引进行迭代的Java8函数样式

Functional programming 使用索引进行迭代的Java8函数样式,functional-programming,java-8,java-stream,Functional Programming,Java 8,Java Stream,我已经练习Java8流和函数式有一段时间了。 有时,我尝试使用流解决一些编程难题。 在这段时间里,我发现了一类任务,我不知道如何用流来解决,只知道用经典方法 此类任务的一个示例是: 给定一个数字数组,找到元素的索引,使数组左半部分的和小于零。 e、 g.对于数组[1,2,3,-1,3,-10,9]答案将是5 我的第一个想法是使用IntStream.generate(0,arr.length)…,但我不知道如何累积值,同时知道索引 因此,问题是: 是否有可能以某种方式在流上累积值,然后有条件地退

我已经练习Java8流和函数式有一段时间了。 有时,我尝试使用流解决一些编程难题。 在这段时间里,我发现了一类任务,我不知道如何用流来解决,只知道用经典方法

此类任务的一个示例是: 给定一个数字数组,找到元素的索引,使数组左半部分的和小于零。 e、 g.对于数组
[1,2,3,-1,3,-10,9]
答案将是
5

我的第一个想法是使用
IntStream.generate(0,arr.length)…
,但我不知道如何累积值,同时知道索引

因此,问题是:

  • 是否有可能以某种方式在流上累积值,然后有条件地退出
  • 那么并行执行是什么呢?这不适合在需要了解元素顺序的地方查找索引的问题

我可以使用我的库(它为Stream API提供了额外的功能)提出一个解决方案,但我对这样的解决方案不太满意:

int[] input = {1, 2, 3, -1, 3, -10, 9};
System.out.println(IntStreamEx.of(
    IntStreamEx.of(input).scanLeft(Integer::sum)).indexOf(x -> x < 0));
// prints OptionalLong[5]
int[]input={1,2,3,-1,3,-10,9};
System.out.println(intstreamx.of(
intstreamx.of(input).scanLeft(Integer::sum)).indexOf(x->x<0);
//打印可选长度[5]

它使用操作来计算前缀和的数组,然后使用操作在该数组上搜索。当
indexOf
短路时,
scanLeft
操作将处理整个输入,并创建一个与输入长度相同的中间数组,这在以命令式方式解决相同问题时是完全不必要的。

我怀疑您的任务是否适合流。您正在寻找的是一个典型的左扫操作,它本质上是一个顺序操作

例如,想象管道中的以下元素:
[1,2,-4,5]
。并行执行可以将其分为两个子部分,即
[1,2]
[-4,5]
。那你会怎么处理它们?您不能单独求和,因为它将产生
[3]
[1]
,然后您就失去了遵守
1+2-4<0
的事实

因此,即使您编写了一个跟踪索引和总和的收集器,它也无法并行运行(我怀疑您甚至不能从中受益),但您可以想象这样一个收集器可以连续使用:

public static Collector<Integer, ?, Integer> indexSumLeft(int limit) {
        return Collector.of(
                () -> new int[]{-1, 0, 0},
                (arr, elem) -> {
                    if(arr[2] == 0) {
                        arr[1] += elem;
                        arr[0]++;
                    }
                    if(arr[1] < limit) {
                        arr[2] = 1;
                    }

                },
                (arr1, arr2) -> {throw new UnsupportedOperationException("Cannot run in parallel");},
                arr -> arr[0]

        );
    }
这仍然会遍历管道的所有元素,因此效率不高

也可以考虑使用<代码>数组。如果数据源是数组,则并行前缀< /C>。只需计算它上面的部分和,然后使用一个流来找到第一个索引,其中和低于限制

Arrays.parallelPrefix(arr, Integer::sum);
int index = IntStream.range(0, arr.length)
                     .filter(i -> arr[i] < limit)
                     .findFirst()
                     .orElse(-1);
parallelPrefix(arr,Integer::sum); int index=IntStream.range(0,arr.length) .filter(i->arr[i] 这里还计算了所有部分和(但并行计算)

简而言之,我将使用一个简单的for循环。

在我的StreamEx库中使用新方法,可能会创建一个惰性解决方案,该解决方案适用于非常长或无限的流。首先,我们可以定义一个新的中间
scanleet
操作:

public static <T> StreamEx<T> scanLeft(StreamEx<T> input, BinaryOperator<T> operator) {
    return input.headTail((head, tail) -> 
               scanLeft(tail.mapFirst(cur -> operator.apply(head, cur)), operator)
                   .prepend(head));
}
这同样适用于无限流(例如,随机数流):

streamx ints=intstreamx.of(new Random(),-100100)
.peek(System.out::println).boxed();
intidx=scanleet(ints,Integer::sum).indexOf(x->x<0);

这将一直运行,直到累积和变为负数并返回相应元素的索引。

流API几乎不可能出现此问题,因为此问题需要跟踪也绑定到元素顺序的非局部状态(所有前缀元素的总和)。流API的设计目的是使并行处理与顺序处理一样简单,但这种操作本质上是顺序的…可能与和重复。很好。我不知道我的名字。非常感谢。
public static <T> StreamEx<T> scanLeft(StreamEx<T> input, BinaryOperator<T> operator) {
    return input.headTail((head, tail) -> 
               scanLeft(tail.mapFirst(cur -> operator.apply(head, cur)), operator)
                   .prepend(head));
}
scanLeft(StreamEx.of(1, 2, 3, -1, 3, -10, 9), Integer::sum).indexOf(x -> x < 0);
StreamEx<Integer> ints = IntStreamEx.of(new Random(), -100, 100)
                                    .peek(System.out::println).boxed();
int idx = scanLeft(ints, Integer::sum).indexOf(x -> x < 0);