如何将函数列表应用于 Java 8 中的值?
Posted
技术标签:
【中文标题】如何将函数列表应用于 Java 8 中的值?【英文标题】:How to apply a list of functions to a value in Java 8? 【发布时间】:2017-11-15 04:37:41 【问题描述】:假设我有这个命令式代码:
List<Function<T, T>> functions = ...
T value = ...
for (Function<T, T> function : functions)
value = function.apply(value);
如何以函数式风格编写此代码(就像 Scala 中的 fold 一样)?
【问题讨论】:
您可以将值和#forEach
装箱functions
列表,但这并没有真正为您带来任何好处(无论如何也不是真正 功能)。总的来说,代码看起来不错,java 是一种命令式语言。
Working with an ArrayList of Functions in Java-8 这会有所帮助
看起来接近this one...
foreach 的问题是,他们需要更新一个共享状态。我需要的是将一个函数的结果传递给链中的下一个函数。
【参考方案1】:
几个小时前刚刚有人问过Consumer
...您可以将它们简化为一个函数并应用它:
@SafeVarargs
private static <T> Function<T, T> combineF(Function<T, T>... funcs)
return Arrays.stream(funcs).reduce(Function.identity(), Function::andThen);
【讨论】:
虽然语义相同,但.reduce(Function::andThen).orElse(Function.identity())
可能会产生更高效的函数。
@srborlongan 之前已经讨论过,见:***.com/questions/44261253/…
好吧,reduce(Function.identity(), Function::andThen)
将使用标识函数作为起点,并为每个生成标识函数和下一个函数的组合函数的函数调用 andThen
(除非标识覆盖 andThen
,它在当前的 JRE 中没有)。相比之下,reduce(Function::andThen)
将返回单个元素流的唯一函数,并且仅在有多个函数时才调用 andThen
,并且仅在流为空时才使用 identity()
。
@srborlongan:没有问题,但也没有保存,因为创建Supplier
并不比调用Function.identity()
便宜。如果这种情况在代码的不同位置多次发生,最终结果将是orElse(Function.identity())
将比orElseGet(Function::identity)
便宜。另见…Function.identity() or t->t
@Didier L:你指的是哪一个?像Function::identity
这样传递给orElseGet(…)
的临时实例很可能会被消除。 Function.identity()
与其他函数组合时,JVM可能会内联它们的代码路径(虽然如果你有多个函数可能会达到阈值),在评估时,CPU性能可能相同,但内存消耗更高只要函数被引用,组合函数就可能一直存在……【参考方案2】:
这是 Eugene 答案的一个变体,只是为了好玩:
public static <T> Function<T, T> combine(List<Function<T, T>> functions)
return new Object()
Function<List<Function<T, T>>, Function<T, T>> combiner = list ->
list.size() == 1 ? list.get(0) :
list.get(0).andThen(this.combiner.apply(list.subList(1, list.size())));
.combiner.apply(functions);
这将创建一个匿名内部类,其属性是递归 lambda。这个属性被命名为combiner
,它是一个高阶函数,它接受一个函数列表作为参数并返回一个函数作为结果。如果列表仅包含一个元素,则此高阶函数返回列表的第一个函数,或者将 andThen
应用于列表的第一个函数,该函数使用递归调用高阶函数的结果从第二个元素开始的函数子列表。
需要匿名内部类,因为递归 lambda 只能定义为类的属性。
不用说,这比使用Function::andThen
二元运算符对列表进行流式传输和减少要复杂得多。此外,递归 lambda 不是免费的:它们使用堆栈进行递归调用。
【讨论】:
除了为 lambdas 递归创建字段之外,Java 8 中的另一种(不太直观)方法是使用 Y combinator。 @srborlongan 或trampolines...以上是关于如何将函数列表应用于 Java 8 中的值?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用依赖于包装它的较短列表的 map 循环较长的列表以将某些函数应用于 Common Lisp 中的较长列表?