实验5:复合函数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yqj2065/article/details/53724942

通过设计复合函数,引入桥接模式。这里以一般性的数学例子,y = f(x),其中 f 表示中x到y的映射。

--------------------------

1.映射f

映射方式多种多样,如

f(x) = x+1
f(x)= x *x
f(x) = (x+1)* (x+1)

因此,映射f可以抽象为函数接口F。Demo与F构成策略模式

package chap4.compositeFunction;
@FunctionalInterface
public interface F {//IntUnaryOperator
   public int f(int x) ; 
}
package chap4.compositeFunction;
import static yqj2065.util.Print.*;
public class Demo{
    //参数化F
    public static int eval(F y, int x) {
        return y.f(x);
    }
    public static void test() {
        F y = x -> x + 1;
        int r = eval(y, 2);       pln(r);
        int r2 = eval(x -> x + 1, 2);        pln(r2);
        int r3 = eval(x -> x*x, y.f(2)); pln(r3);
        int r4 = eval(x -> x*x, eval(x -> x + 1, 2)); pln(r4);        
    }
}

 

 

2.g(f(x))

 

 

在数学中,有一个重要概念:复合函数(composite function)——经过多次映射的函数,如z = g(f(x))。如果f(x) = x+1,g(x) = x*x,则g(f(x))  = f(x)* f(x) =( x+1)* ( x+1)。

当讨论复合函数时,需要将多个函数复合成为一个最终的函数,再用于计算其某个参数x时的值。对于定义复合函数h(g(f(x))),按照从内到外的顺序,也可以按照从外到内的顺序。

按照从内到外的顺序,即首先定义函数f(x)是什么,然后再使用f(x)去定义g(f(x))。这种代入的方式十分自然。

    public static void compsite(){
        F y = x -> x + 1; //函数 y = x+1
        F comp = x -> y.f(x) * y.f(x);//z=y*y
        int r =comp.f(2); pln(r);
    }

 

3.高阶函数G

可以按照从外到内的顺序,定义复合函数h(g(f(x)))。也就是说,在定义z =g(y)时,程序员还不知道y是什么。其思路是:对于z =g(y),不管y具体是什么,z最终是一个x的函数即F的对象,即它对参数y进行某种操作后得到的是一个F的对象。因此,将上述思路抽象出接口G,如例程4-7所示。实现类Z1可以让读者对G有直观的认识,它对参数y求y*y后返回一个F的对象。

package chap4.compositeFunction;
@FunctionalInterface
public interface G {//IntUnaryOperator
   public F g(F y); 
}

class Z1 implements G{
    @Override  public F g(F y){
        return (x) -> y.f(x) * y.f(x);
    }
}

按照上面的例子,先将任意的y按照y*y复合,则
        G z = y ->{
            return (x) -> y.f(x) * y.f(x); //
        };
外层的λ表达式,定义了对于任意的函数y的复合方式,即y*y。该λ表达式的返回值说明,z是x的函数。

package higherOrderFunction;
import static util.Print.*;
public class MathDemo { 
    public static void main(String[] a) {
        G z = y -> x -> y.f(x) * y.f(x);
        F y = x -> x + 1;
        F f3 = z.g(y);
        pln(f3.f(2));
    }
}

理解其计算过程,非常重要的一点是,λ表达式只是一个引用,正如创建一个String对象"hello"并不会执行"hello"..startsWith("h")一样,λ表达式的函数体并不会执行。

    public static void testGn(){
        G h = y -> x -> y.f(x) * y.f(x) + 2 * y.f(x);//
        G g = y -> x -> y.f(x) * y.f(x);
        G z = y -> x -> y.f(x)+1;
        F comp = h.g(g.g(z.g(F.identity)));

        int r =comp.f(2);pln(r);
    }

可以定义一系列g1(y)、g2(y)、g3(y),按照希望的顺序对它们进行复合。最后都需要一个F对象如y作为落脚点。可以在接口F中预定义一个恒等函数 identity作为固定的落脚点。

 

为了观察执行过程,建议在代码中增加打印语句:

 

        G z = y -> {
            pln("z:" + y);
            return (x) -> {
                pln("in z: x=" + x);
                int i = y.f(x);
                pln("in z: i=" + i);
                return i * i;
            };
        };

 

 

 

 

 

 

 


另外,可以使用通用函数接口替代F、G。

   public static void main(String[] a) {
//        G z = y -> x -> y.f(x) * y.f(x);
        Function<IntUnaryOperator,IntUnaryOperator> g3 = y ->  x -> y.applyAsInt(x) * y.applyAsInt(x);
        IntUnaryOperator op =x -> x + 1;
//        F y = x -> x + 1;
        pln(g3.apply(op).applyAsInt(2));

    }

 

 

或者:

 

 

    public static void main(String[] a) {
        Function<Integer, Integer> f = x -> x + 1;
        Function<Integer, Integer> g = x -> x * x;
        pln(g.compose(f).apply(2));

    }

 

 

很多程序员习惯于方法的返回值是一个实体,如int、String,而对于返回一个函数则不习惯。本文介绍这种高阶函数

 

以函数为参数、或以函数为返回值的函数是一个高阶函数。

 

 

 

 

 

展开阅读全文

没有更多推荐了,返回首页