[英]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.