簡體   English   中英

相當於 Java 8 中 Scala 的 foldLeft

[英]Equivalent of Scala's foldLeft in Java 8

Java 8 中 Scala 的foldLeft相當於什么?

我很想認為它是reduce ,但是 reduce 必須返回與它所減少的相同類型的東西。

例子:

import java.util.List;

public class Foo {

    // this method works pretty well
    public int sum(List<Integer> numbers) {
        return numbers.stream()
                      .reduce(0, (acc, n) -> (acc + n));
    }

    // this method makes the file not compile
    public String concatenate(List<Character> chars) {
        return chars.stream()
                    .reduce(new StringBuilder(""), (acc, c) -> acc.append(c)).toString();
    }
}

在上面的代碼中的問題是acc umulator: new StringBuilder("")

因此,有人可以指出我正確的foldLeft /fix my code 等價物嗎?

Java 8 的 Stream API 中沒有foldLeft等價物。 正如其他人所指出的, reduce(identity, accumulator, combiner)接近,但它與foldLeft不等價,因為它需要結果類型B與自身組合並具有關聯性( foldLeft ,類似於幺半群),一個屬性不每種類型都有。

對此還有一個增強請求:添加Stream.foldLeft()終端操作

要了解為什么 reduce 不起作用,請考慮以下代碼,您打算在其中執行一系列以給定數字開頭的算術運算:

val arithOps = List(('+', 1), ('*', 4), ('-', 2), ('/', 5))
val fun: (Int, (Char, Int)) => Int = {
  case (x, ('+', y)) => x + y
  case (x, ('-', y)) => x - y
  case (x, ('*', y)) => x * y
  case (x, ('/', y)) => x / y
}
val number = 2
arithOps.foldLeft(number)(fun) // ((2 + 1) * 4 - 2) / 5

如果您嘗試編寫reduce(2, fun, combine) ,您可以傳遞什么組合器函數來組合兩個數字? 將兩個數字加在一起顯然不能解決問題。 此外,值2顯然不是標識元素。

請注意,任何需要順序執行的操作都不能用reduce表示。 foldLeft實際上比reduce更通用:您可以使用foldLeft實現reduce ,但不能使用reduce實現foldLeft

更新:

這是修復代碼的初步嘗試:

public static String concatenate(List<Character> chars) {
        return chars
                .stream()
                .reduce(new StringBuilder(),
                                StringBuilder::append,
                                StringBuilder::append).toString();
    }

它使用以下reduce方法

<U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);

這聽起來可能令人困惑,但如果您查看 javadoc,有一個很好的解釋可以幫助您快速掌握細節。 減少相當於以下代碼:

U result = identity;
for (T element : this stream)
     result = accumulator.apply(result, element)
return result;

如需更深入的解釋,請查看此來源

但是這種用法是不正確的,因為它違反了 reduce 的約定,其中規定累加器應該是一個關聯的、無干擾的、無狀態的函數,用於將附加元素合並到結果中 換句話說,由於身份是可變的,因此在並行執行的情況下結果將被破壞。

正如下面的評論中所指出的,正確的選項是使用如下縮減:

return chars.stream().collect(
     StringBuilder::new, 
     StringBuilder::append, 
     StringBuilder::append).toString();

供應商StringBuilder::new將用於創建可重用的容器,這些容器稍后將被合並。

您正在尋找的方法是java.util.Stream.reduce ,特別是具有三個參數、標識、累加器和二元函數的重載。 這是 Scala 的foldLeft的正確等價物。

但是,你不能使用Java的reduce這種方式,也沒有Scala的foldLeft對這一問題。 改用collect

它可以通過使用收集器來完成:

public static <A, B> Collector<A, ?, B> foldLeft(final B init, final BiFunction<? super B, ? super A, ? extends B> f) {
    return Collectors.collectingAndThen(
            Collectors.reducing(Function.<B>identity(), a -> b -> f.apply(b, a), Function::andThen),
            endo -> endo.apply(init)
    );
}

用法示例:

IntStream.rangeClosed(1, 100).boxed().collect(foldLeft(50, (a, b) -> a - b));  // Output = -5000

對於您的問題,這符合您的要求:

public String concatenate(List<Character> chars) {
        return chars.stream()
                .collect(foldLeft(new StringBuilder(), StringBuilder::append)).toString();
}

其他人是正確的,但沒有等價物。 這是一個接近的實用程序-

<U, T> U foldLeft(Collection<T> sequence, U identity, BiFunction<U, ? super T, U> accumulator) {
    U result = identity;
    for (T element : sequence)
        result = accumulator.apply(result, element);
    return result;
}

您使用上述方法的情況看起來像-

public String concatenate(List<Character> chars) {
    return foldLeft(chars, new StringBuilder(""), StringBuilder::append).toString();
}

或者沒有 lambda 方法引用糖,

public String concatenate(List<Character> chars) {
    return foldLeft(chars, new StringBuilder(""), (stringBuilder, character) -> stringBuilder.append(character)).toString();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM