专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Java函数接口实现函数组合及装饰器模式

摘要: 通过求解 (sinx)^2 + (cosx)^2 = 1 的若干写法,逐步展示了如何从过程式的写法转变到函数式的写法,并说明了编写“【接受函数参数】并返回【能够接受函数参数的函数】的【高阶函数】”的一点小技巧。

难度: 中级。

代码在此,先领会一下


package zzz.study.function.decrator; import java.util.Arrays; import java.util.List; import java.util.function.BiFunction; import java.util.function.Function; import static java.lang.Math.*; /** * Created by shuqin on 17/6/29. */ public class FunctionImplementingDecrator { public static void main(String[] args) { // 求解 (sinx)^2 + (cosx)^2 = 1 的若干写法 double x= 30; System.out.println(Math.pow(sin(x),2) + Math.pow(cos(x), 2)); System.out.println(pow(Math::sin, 2).apply(x) + pow(Math::cos, 2).apply(x)); double res = op(pow(Math::sin, 2).apply(x), pow(Math::cos, 2).apply(x)).apply((a,b) -> a+b); System.out.println(res); double res2 = op(pow(Math::sin, 2), pow(Math::cos, 2), x).apply((a,b) -> a+b); System.out.println(res2); Function<Double,Double> sumSquare = op(pow(Math::sin, 2), pow(Math::cos, 2)).apply((a,b)->a+b); System.out.println(sumSquare.apply(x)); Function<Double,Double> another = op(compose((d)->d*d, Math::sin), compose((d)->d*d, Math::cos)).apply((a,b)->a+b); System.out.println(another.apply(x)); Function<Double,Double> third = compose(d->d*d, d->d+1, d->d*2, d->d*d*d); // (2x^3+1)^2 System.out.println(third.apply(3d)); } /** 将指定函数的值封装幂次函数 pow(f, n) = (f(x))^n */ public static <T> Function<T, Double> pow(final Function<T,Double> func, final int n) { return x -> Math.pow(func.apply(x), (double)n); } /** 对给定的值 x,y 应用指定的二元操作函数 */ public static <T> Function<BiFunction<T,T,T>, T> op(T x, T y) { return opFunc -> opFunc.apply(x, y); } /** 将两个函数使用组合成一个函数,这个函数接受一个二元操作函数(eg +, -, * , /) */ public static <T> Function<BiFunction<T,T,T>, T> op(Function<T,T> funcx, Function<T,T> funcy, T x) { return opFunc -> opFunc.apply(funcx.apply(x), funcy.apply(x)); } /** 将两个函数组合成一个叠加函数, compose(f,g) = f(g) */ public static <T> Function<T, T> compose(Function<T,T> funcx, Function<T,T> funcy) { return x -> funcx.apply(funcy.apply(x)); } /** 将若干个函数组合成一个叠加函数, compose(f1,f2,...fn) = f1(f2(...(fn))) */ public static <T> Function<T, T> compose(Function<T,T>... extraFuncs) { if (extraFuncs == null || extraFuncs.length == 0) { return x->x; } return x -> Arrays.stream(extraFuncs).reduce(y->y, FunctionImplementingDecrator::compose).apply(x); } public static <T> Function<BiFunction<T,T,T>, Function<T,T>> op(Function<T,T> funcx, Function<T,T> funcy) { //return opFunc -> { return aT -> opFunc.apply(funcx.apply(aT), funcy.apply(aT)); }; return opFunc -> aT -> opFunc.apply(funcx.apply(aT), funcy.apply(aT)); /* Equivalent to return new Function<BiFunction<T, T, T>, Function<T, T>>() { @Override public Function<T, T> apply( BiFunction<T, T, T> opFunc) { return new Function<T, T>() { @Override public T apply(T aT) { return opFunc.apply(funcx.apply(aT), funcy.apply(aT)); } }; } };*/ } }

编写“【接受函数参数】并返回【能够接受函数参数的函数】的【高阶函数】”的一点小技巧:直接用 lambda 表达式的角度去思考,辅以数学推导。

比如要编写一个函数 F(G,H) , 接受两个一元函数参数 G(x) , H(x) ,返回一个函数: R(op) ,R(op) 接受一个二元操作函数 op(x,y),返回一个一元函数 T(x)。即:F(G(x), H(x)) = R(op)(x) = op(G, H)(x) = T(x) : x -> op(G(x), H(x))

看上去挺绕的!那么该怎么写呢?

先理一理: R(op)(x) = G(x) op H(x) = op(G, H)(x) 。由于 R(op) 是接受一个二元操作函数 opFunc, 那么应该有 opFunc -> opFunc(G, H) ; 完成了一半! 注意到,opFunc(G,H) 的结果应当是一个单元函数 T(x) ,opFunc(G,H) = x -> T(x) , T(x) = op(G(x), H(x)) ; 于是最终有 F(G(x), H(x)) = opFunc -> { x -> opFunc(G(x), H(x)) }

public static <T> Function<BiFunction<T,T,T>, Function<T,T>> op(Function<T,T> funcx, Function<T,T> funcy) {
    return opFunc -> { return aT -> opFunc.apply(funcx.apply(aT), funcy.apply(aT)); };
}

只要是赋值给函数接口,一定有 (x1,x2,…,xn) -> F(x1,x2,…,Xn) 形式。 然后无非是这种形式的组合及嵌套。 经过一通脑筋急转弯之后,似乎摸到了一点窍门。化简成 lambda 表达式的形式是(IDE会自动提示):

public static <T> Function<BiFunction<T,T,T>, Function<T,T>> op(Function<T,T> funcx, Function<T,T> funcy) {
    return opFunc -> aT -> opFunc.apply(funcx.apply(aT), funcy.apply(aT));
}

第一种形式更容易理解, 第二种形式比较简洁。显然, -> 符号是右结合优先的。

由此可见,函数式编程可以通过凝练的代码形式将函数能力组合起来,构建强大的抽象表达能力,对于消除重复代码及框架设计有很大的益处。同时,使用函数编程需要经常从“函数及组合的层面”去思考计算,而不是从通常的“求值层面”去思考计算。这无疑对抽象思维能力有更高的要求。

不是每个知识点都要正儿八经地写上一篇文章,多尝试摸索窍门反而是妙法

文章永久链接:https://tech.souyunku.com/20843

未经允许不得转载:搜云库技术团队 » Java函数接口实现函数组合及装饰器模式

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们