实验5:复合函数

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

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


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

f(x) = x+1
f(x)= x *x
f(x) = (x+1)* (x+1) //这是一个函数,不是复合函数

对映射f的抽象(简化起见,f设为一元函数,参数为int),对参数x进行某种操作并返回一个int,由函数接口F封装,Test与F构成策略模式。函数接口F的实现类定义进行何种具体的操作,为了给不熟悉lambda表达式的读者增加依靠感,可以编写F的独立子类如Y1描述了f(x)= x *x。

package chap4.compositeFunction;
@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);        
    }    
}  

当定义了F对象x -> x + 1——即数学函数f(x) = x+1后,可以直接利用y.f(2)求值,也可以调用eval(y, 2)。观察求值函数eval(F, int),可以将一个数学函数的计算结果(一个int值),作为另外一个函数的第2个参数,如语句

eval(x -> x*x, eval(x -> x + 1, 2))

求值过程为:2为实参求值f(x) = x+1,以结果3为实参求值f(x) = x*x。

2.复合函数g(f(x))

1. 代入法

定义复合函数h(g(f(x))),最简单的方式是按照从内到外的顺序来进行,首先定义函数f(x)是什么,然后再使用f(x)去定义g(f(x))。这种代入的方式十分自然。例如,

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

这段代码中定义了策略F的多个实现y和comp,前面定义的实现即y,参与到后面定义的实现即comp中。在多次使用策略F的时候,comp对象将多个策略选择串接起来。注意:将多个策略选择串接起来并不意味着获得的策略F的对象是展开后的x*x+2x+1,而是y*y(其中y = x+1)。这段代码中程序员手工将多个策略选择串接起来(将两个函数进行复合)。

2. F的复合方法

可以在F接口中定义一个默认方法用于函数复合,程序员可以先定义一系列F对象,再按照任意的顺序和次数加以复合。

   public default F com(F y) {

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

   }

 

3. 更高级的函数接口G

在F的基础上,可以定义更高级的函数接口G。F定义了函数映射int→int,而G定义了函数的复合映射F→F。

package chap4.compositeFunction;
@FunctionalInterface public interface F{ //  IntUnaryOperator 
    public  int f(int x);
    public default F com(F y) {
        return (int x) -> this.f(y.f(x));
   }
   F identity = x->x;
}
package chap4.compositeFunction;
@FunctionalInterface 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);
    }
}

G的抽象方法和F的默认方法com(F )的区别是:F的默认方法表示将本对象的运算方式用于参数对象,得到一个复合的F对象;而G的抽象方法,用于创建数学表示g(f(x))中的g对象,表示G的实现类将给出某种运算方式,并用于参数对象。

可以定义一系列F对象,使用F的默认方法com(F )按照任意的顺序加以复合;也可以定义一系列G对象,使用G对象的复合规则com(F ) 按照任意的顺序加以复合。

通常,G的实现类不会被编写成独立的类,而是使用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)。

复合函数的最后一步,都需要一个F对象如作为落脚点。可以在接口F中预定义一个命名常量如恒等函数F.identity作为固定的F。

//例程 4 12 Test中的各种测试
    public static void fTest(){
        F y = x -> x + 1; //函数 y = x+1
        F comp = x -> y.f(x) * y.f(x);//comp =y*y
        int r =comp.f(2); pln(r);//9

        F z = x -> x * x;        
        F composeF = y.com(z);//y +1,y=x * x
        pln(composeF.f(2));//2*2+1
        composeF = z.com(y); // x * x
        pln(composeF.f(2));//2*2+1  

    } 
    public static void gTest(){
        G z = new Z1();//z = y*y
        F f = x -> x + 1;
        F comp = z.com(f);
        int r =comp.f(2);pln(r);

        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 w = y -> x -> y.f(x)+1;
        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<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));

    }

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值