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

Wesley13
• 阅读 603

分享编程技能、互联网技术、生活感悟、打造干货分享平台,将总结的技术、心得、经验分享给大家,这里不只限于技术!还有职场心得、生活感悟、以及面经****。点击上方 "杨守乐" ,选择“置顶公众号”,第一时间送达!

摘要: 通过求解 (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));}

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

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

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

如果您觉得不错,请别忘了转发、分享、点赞让更多的人去学习, 您的举手之劳,就是对小乐最好的支持,非常感谢!

如何您想进技术群和大牛们交流,关注公众号在后台回复 “加群”,或者 “学习” 即可

来自:琴水玉

链接:

http://www.cnblogs.com/lovesqcc/p/7107558.html

著作权归作者所有。本文已获得授权。欢迎投稿。

每日英文

Though I fall, I will rise again. Don’t lose hope. You never know what tomorrow will bring. 

就算我失败了,我也会再起。不要失去希望,你永远不知道明天会带给你什么。

乐乐有话说

生活总是让我们遍体鳞伤,但请相信,你今天受的苦,吃的亏,担的责,扛的罪,忍的痛,到最后都会变成光,照亮你的路。

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

             推荐阅读
        
            
            
            
        
            
            
            
        
            
            
            
        
            
            
            
       
           
           
           
       
           
           
           
        
            
            
            2017年阿里、腾讯、百度、华为、京东、搜狗和滴滴最新面试题汇集
        
            
            
            红黑树详细分析,看了都说好对一致性Hash算法,Java代码实现的深入研究
        
            
            
            漫画:什么是真正的程序员?Android面试题算法之二叉树精练代码:一次Java函数式编程的重构之旅一篇文章搞定面试中的二叉树2018 年,去百度面试 Java 后端的一次面试经历
       
           
           
           
      
          
          
          
     
         
         
         
    
        
        
        
  
      
      
      
 
     
     
     

=================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================

       看完本文有收获?请转发分享给更多人关注「杨守乐」,提升技能
  
      
      
      
 
     
     
     

====================================================================================================================================================================================================================================

本文分享自微信公众号 - 程序员小乐(study_tech)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Stella981 Stella981
3年前
GitHub 上有哪些适合新手跟进的优质项目?
!(https://oscimg.oschina.net/oscnet/011f28e3bc332010e1442e6c00ed344805d.jpg)点击上方“迈微电子研发社”,选择“星标★”公众号重磅干货,第一时间送达!(https://oscimg.oschina.net/oscnet/cd44ba75f
Wesley13 Wesley13
3年前
IM消息系统的设计和实现
点击关注上方“JAVA开发大本营”,设为“置顶或星标”,第一时间送达技术干货。!(https://oscimg.oschina.net/oscnet/6cc35fe4f21c47a2b27b53fbdd52d064.png)全文预计阅读6分钟!(https://osci
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Docker 部署SpringBoot项目不香吗?
  公众号改版后文章乱序推荐,希望你可以点击上方“Java进阶架构师”,点击右上角,将我们设为★“星标”!这样才不会错过每日进阶架构文章呀。  !(http://dingyue.ws.126.net/2020/0920/b00fbfc7j00qgy5xy002kd200qo00hsg00it00cj.jpg)  2
Stella981 Stella981
3年前
200的大额人民币即将面世?央行:Yes!
点击上方蓝字关注我们!(https://oscimg.oschina.net/oscnet/2a1c2ac00bf54458a78c48a6c2e547d5.png)点击上方“印象python”,选择“星标”公众号重磅干货,第一时间送达!!(
可莉 可莉
3年前
200的大额人民币即将面世?央行:Yes!
点击上方蓝字关注我们!(https://oscimg.oschina.net/oscnet/2a1c2ac00bf54458a78c48a6c2e547d5.png)点击上方“印象python”,选择“星标”公众号重磅干货,第一时间送达!!(
Wesley13 Wesley13
3年前
Java开发者必须掌握的20个Spring 常用注解
!(https://oscimg.oschina.net/oscnet/41eb49a7b02f4971bd9b22aa02c5aef8.gif)专注于编程、互联网动态。最终将总结的技术、心得、经验(数据结构与算法、源码分析等)分享给大家,这里不只限于技术!还有职场心得、生活感悟、以及面经。点击上方"java大数据修炼之