摘要: 經過求解 (sinx)^2 + (cosx)^2 = 1 的若干寫法,逐步展現瞭如何從過程式的寫法轉變到函數式的寫法,並說明了編寫「【接受函數參數】並返回【可以接受函數參數的函數】的【高階函數】」的一點小技巧。java
難度: 中級。
編程
代碼在此,先領會一下~~數組
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 表達式的角度去思考,輔以數學推導。app
好比要編寫一個函數 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))框架
看上去挺繞的!那麼該怎麼寫呢?ide
先理一理: 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)); }
第一種形式更容易理解, 第二種形式比較簡潔。顯然, -> 符號是右結合優先的。ui
因而可知,函數式編程能夠經過凝練的代碼形式將函數能力組合起來,構建強大的抽象表達能力,對於消除重複代碼及框架設計有很大的益處。同時,使用函數編程須要常常從「函數及組合的層面」去思考計算,而不是從一般的「求值層面」去思考計算。這無疑對抽象思惟能力有更高的要求。設計
不是每一個知識點都要正兒八經地寫上一篇文章,多嘗試摸索竅門反而是妙法 :)