Java 8 中 Scala 的 foldLeft 的等价物
Equivalent of Scala's foldLeft in Java 8
Scala 的伟大 foldLeft
在 Java 8 中的等价物是什么?
我很想认为它是 reduce
,但 reduce 必须 return 与它所减少的对象类型相同的东西。
示例:
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
/修复我的代码的正确等价物吗?
更新:
这是修复代码的初步尝试:
public static String concatenate(List<Character> chars) {
return chars
.stream()
.reduce(new StringBuilder(),
StringBuilder::append,
StringBuilder::append).toString();
}
它使用以下 reduce method:
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
这听起来可能令人困惑,但如果您查看 javadocs,会有一个很好的解释,可以帮助您快速掌握细节。缩减等同于以下代码:
U result = identity;
for (T element : this stream)
result = accumulator.apply(result, element)
return result;
如需更深入的解释,请查看this source。
这种用法是不正确的,因为它违反了 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
。
Java 8 的 Stream API 中没有 foldLeft
的等效项。正如其他人指出的那样, reduce(identity, accumulator, combiner)
接近,但它不等同于 foldLeft
因为它需要结果类型 B
与自身结合并具有关联性(换句话说,类似于幺半群) , 属性 并非每种类型都有。
还有一个增强请求:add Stream.foldLeft() terminal operation
要了解为什么 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
显然不是 identity 元素。
请注意,任何需要顺序执行的操作都不能用reduce
表示。 foldLeft
实际上比 reduce
更通用:您可以用 foldLeft
实现 reduce
,但不能用 reduce
实现 foldLeft
。
其他人是正确的,但没有对等的。这是一个接近的实用程序-
<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();
}
可以使用收集器来完成:
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();
}
Scala 的伟大 foldLeft
在 Java 8 中的等价物是什么?
我很想认为它是 reduce
,但 reduce 必须 return 与它所减少的对象类型相同的东西。
示例:
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
/修复我的代码的正确等价物吗?
更新:
这是修复代码的初步尝试:
public static String concatenate(List<Character> chars) {
return chars
.stream()
.reduce(new StringBuilder(),
StringBuilder::append,
StringBuilder::append).toString();
}
它使用以下 reduce method:
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
这听起来可能令人困惑,但如果您查看 javadocs,会有一个很好的解释,可以帮助您快速掌握细节。缩减等同于以下代码:
U result = identity;
for (T element : this stream)
result = accumulator.apply(result, element)
return result;
如需更深入的解释,请查看this source。
这种用法是不正确的,因为它违反了 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
。
Java 8 的 Stream API 中没有 foldLeft
的等效项。正如其他人指出的那样, reduce(identity, accumulator, combiner)
接近,但它不等同于 foldLeft
因为它需要结果类型 B
与自身结合并具有关联性(换句话说,类似于幺半群) , 属性 并非每种类型都有。
还有一个增强请求:add Stream.foldLeft() terminal operation
要了解为什么 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
显然不是 identity 元素。
请注意,任何需要顺序执行的操作都不能用reduce
表示。 foldLeft
实际上比 reduce
更通用:您可以用 foldLeft
实现 reduce
,但不能用 reduce
实现 foldLeft
。
其他人是正确的,但没有对等的。这是一个接近的实用程序-
<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();
}
可以使用收集器来完成:
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();
}