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

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

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

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

Java函数接口实现函数组合及装饰器模式的更多相关文章

  1. Java设计模式07:常用设计模式之装饰器模式(结构型模式)

    1. Java之装饰器模式(Decorator Pattern) (1)概述:     装饰模式在Java种使用也很广泛,比如我们在重新定义按钮.对话框等时候,实际上已经在使用装饰模式了.在不必改变原 ...

  2. Java设计模式从精通到入门二 装饰器模式

    介绍 ​ 我尽量用最少的语言解释总结: ​ Java23种设计模式之一,属于结构型模式,允许向一个现有的对象添加新的功能,不改变其结构. 应用实例: ​ 给英雄联盟种的射手,添加不同的装备.先装备攻速 ...

  3. 使用IntelljIDEA生成接口的类继承图及装饰器模式

    类图生成方法 以一个装饰器模式实现数学运算的例子为例. 安装 Intellj Ultimate , lience server: http://xdouble.cn:8888/ 在类上右键点击 cla ...

  4. 装饰器模式(Decorator)

    转自http://blog.csdn.net/hust_is_lcd/article/details/7884320 1.认识装饰器模式 装饰模式能够实现动态的为对象添加功能,是从一个对象外部来给对象 ...

  5. 设计模式之装饰器模式(decorator pattern)

    装饰器模式主要对现有的类对象进行包裹和封装,以期望在不改变类对象及其类定义的情况下,为对象添加额外功能.是一种对象结构型模式.需要注意的是,该过程是通过调用被包裹之后的对象完成功能添加的,而不是直接修 ...

  6. c#设计模式之装饰器模式(Decorator Pattern)

    引子 在面向对象语言中,我们常常会听到这样一句话:组合优于继承.那么该如何去理解这句话呢? 下面我将以游戏装备为模型用简单的代码去展示它 先创建一个装备的抽象类,然后创建刀枪2个具体的业务子类 pub ...

  7. JAVA装饰器模式

    Java程序员们应该对java.io对不会陌生,因为java.io包采用了装饰器模式. 一.定义: Decorator装饰器,顾名思义,就是动态地给一个对象添加一些额外的职责,就好比为房子进行装修一样 ...

  8. Java设计模式12:装饰器模式

    装饰器模式 装饰器模式又称为包装(Wrapper)模式.装饰器模式以多客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 装饰器模式的结构 通常给对象添加功能,要么直接修改对象添加相应的功能, ...

  9. java IO之 字符流 (字符流 = 字节流 + 编码表) 装饰器模式

    字符流 计算机并不区分二进制文件与文本文件.所有的文件都是以二进制形式来存储的,因此, 从本质上说,所有的文件都是二进制文件.所以字符流是建立在字节流之上的,它能够提供字符 层次的编码和解码.列如,在 ...

随机推荐

  1. coocs2d-html5在使用cocoseditor时调用设备的accelerometer来使用重力感应

    在使用大牛touchsnow开发的插件cocoseditor开发游戏时遇到了一些问题,然后就试着解决.近期想试下coocs2d-html5是否能使用重力感应,发现是能够的.只是这个仅仅能在移动真机上測 ...

  2. vc让界面保持最上层

    vc让界面保持最上层.事实上就一个函数就ok了, ::SetWindowPos(AfxGetMainWnd()->m_hWnd,HWND_TOPMOST,-1,-1,-1,-1,0);

  3. 终极大招——Scrapy框架

    Scrapy框架 Scrapy 是一个开源和协作的框架,其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的,使用它可以以快速.简单.可扩展的方式从网站中提取所需的数据.但目前Scrapy的用途 ...

  4. 015-线程同步-synchronized几种加锁方式、Java对象头和Monitor、Mutex Lock、JDK1.6对synchronized锁的优化实现

    一.synchronized概述基本使用 为确保共享变量不会出现并发问题,通常会对修改共享变量的代码块用synchronized加锁,确保同一时刻只有一个线程在修改共享变量,从而避免并发问题. syn ...

  5. wxPython:消息对话框MessageDialog

    wxMessageDialog(wxWindow* parent, const wxString& message, const wxString& caption = "M ...

  6. Java中的字段和属性

    Java中的属性,通常可以理解为get和set方法.而字段,通常叫做“类成员”. 属性只局限于类中方法的声明,并不与类中其他成员相关.例如:void setA(String s){}String ge ...

  7. Cell complex单元复合形

    概念 (1)Piecewise linear complex (PLC) 分段线性复合形 (2)Cell complex 单元复合形 [1] (元胞复合形) (3)Linear Cell Comple ...

  8. 【LNMP】提示Nginx PHP “No input file specified”错误的解决办法

    原理: 任何对.php文件的请求,都简单地交给php-cgi去处理,但没有验证该php文件是否存在. PHP文件不存在,没办法返回普通的404错误,它返回 一个404,并带上一句”No input f ...

  9. Scala数据类型的继承结构

    Scala中,所有的值都是类对象,而所有的类,包括值类型,都最终继承自一个统一的根类型Any.统一类型,是Scala的又一大特点.更特别的是,Scala中还定义了几个底层类(Bottom Class) ...

  10. 【UML】-NO.41.EBook.5.UML.1.001-【UML 大战需求分析】- 类图(Class Diagram)

    1.0.0 Summary Tittle:[UML]-NO.41.EBook.1.UML.1.001-[UML 大战需求分析]- 类图 Style:DesignPattern Series:Desig ...