实验5:复合函数

实验5:定义任意的复合函数。

理解:一个策略选择融为另外一个策略选择的一部分。桥接和装饰模式。


数学中的函数y = f(x),其中 f 表示从x到y的映射。函数的映射方式无穷无尽,如

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

首先,用函数接口F定义一种简单的映射,即把映射f抽象为Java方法,对参数x进行某种操作(简化起见,f设为一元函数,参数为int)并返回一个int。函数接口F的实现类定义进行何种具体的操作,F的实现类无穷多,例程4-10中编写的一个独立的子类如Y1描述了f(x)= x *x。提供Y1便于不熟悉lambda表达式读者,用Y1这种传统的代码增加依靠感。

函数接口F是抽象策略,而Demo与F构成策略模式,Demo的求值函数eval(F, int) 将函数接口F作为形参。

package chap4.bridgeP.bridgeP2;
@FunctionalInterface
public interface F {//IntUnaryOperator
   public int f(int x) ; 
}//Y1: return  x *x;

import static yqj2065.util.Print.*;
public class Test{ //FContext
    //参数化F
    public static int eval(F y, int x) { //函数y作用于x一次
        return y.f(x);
    }
    public static void test() {
        F y = x -> x + 1;
        int r = eval(y, 2);       pln(r);// r =y.f(2);
        int r2 = eval(x -> x + 1, 2);        pln(r2); //F对象直接作为实参
        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)的求值过程,同样采取上面语句的执行方式,将一个函数的计算结果用于另外一个函数的计算中。即先将2参与f(x) = x+1计算得到结果3,再将结果3参与f(x) = x*x计算。复合函数的求值过程并非先把(x+1)* (x+1)转化成x*x+2x+1再对参数求值。

复合函数则是经过多次映射的函数,例如函数z = g(f(x))。如果f(x) = x+1,g(x) = x*x,则g(f(x))  = f(x)* f(x) =( x+1)* ( x+1)。定义复合函数,需要将多个函数先复合成为一个最终的F对象,再用于计算某个参数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);
    }

这段代码中程序员手工将多个策略选择串接起来(将两个函数进行复合)。可以在F接口中定义一个默认方法用于函数复合。

   public default F com(F y) {

        return (int x) -> this.f(y.f(x));

   }

于是,

        F y = x -> x + 1; //函数 y = x+1       

        F z = x -> x * x;

        F u = x -> x * x + 2 * x;

        F composeF = u.com(z.com(y));

        pln(composeF.f(2));

3.高阶函数G

在F的基础上,可以定义更高级的函数接口G。

F定义了函数映射int→int,而G定义了函数的复合映射F→F。

G的抽象方法和F的默认方法com(F )看起来是一样的,实质上不同。F的默认方法,表示一个F的对象将用方法体与参数对象复合;而G的抽象方法,用于创建数学表示g(f(x))中的g对象,对于z=g(y),不管y具体是什么,z最终是一个x的函数即F的对象,即它对参数y进行复合后得到的是一个F的对象。

package bridge.compositeFunction;
public interface G {
   public F com (F y);
} 
class Z1 implements G{
    @Override  public F com (F y){
        return (x) -> y.f(x) * y.f(x);
    }
}

1. 通过G进行函数复合

使用函数接口G定义复合函数,其流程是:可以先定义一个G,再定义一个F,然后调用G的方法com(F)将两次策略选择串接起来。这就是所谓从外到内的复合。如

        G z = new Z1();
        F y = x -> x + 1;//现在知道y是什么了
        F comp = z. com (y);

通常,G的实现类不会被编写成独立的类,因此上面的定义复合函数流程,有如下的变体:

(1)G z声明语句的变化。通常使用lambda表达式创建G的实现类对象。

        G z = y -> {

            return (x) -> y.f(x) * y.f(x);//z = y * y

        };//或者

        G z = y -> x -> y.f(x) * y.f(x);

外层的λ表达式,定义了对于任意的函数y的复合方式y*y,其返回值为内层的λ表达式,即(x) -> y.f(x) * y.f(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.com(y);
        pln(f3.f(2));
    }
}

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

public interface F{    
    public  int f(int x);
    public default F com(F y) {
        return (int x) -> this.f(y.f(x));
   }
   F identity = x->x;
}
//Test    

    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.com(g.com(z.com(F.identity)));
        comp = z.com(g.com(h.com(F.identity)));
        pln(comp.f(2));

    }

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

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

 

        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。

//import java.util.function.*;

   public static void main(String[] a) {
//        G z = y -> x -> y.f(x) * y.f(x);
        Function 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 f = x -> x + 1;
        Function g = x -> x * x;
        pln(g.compose(f).apply(2));

    }

 

你可能感兴趣的:(#,面向对象设计(Java))