首页 > 编程语言 > 详细

java函数式编程--柯里化(Currying),闭包

时间:2017-03-05 23:20:21      阅读:309      评论:0      收藏:0      [点我收藏+]
近年来函数式编程趋热,在JavaScript上积累了一定的经验后,我也尝试着用函数式编程的思想来重新理解java编程。
闭包
闭包在Js中作为Js的入门概念,指的是函数的执行环境依赖于创建时的一系列作用域链的现象。
var v="a";
var fn=(function(){
        v="b";
     return  function(){
          v="c";
          console.log(v);
     }     
})();
fn();
当我们分别注释v的“c”,“b”的赋值时,v的值将会向外寻找,对应的值也是“c”,“b”,“a”
也就是说fn带走了当时的数值作用域。
由于闭包的这一特性,我们得以访问函数内部变量。
而在Java中,我们也可以找到类似的功能:
public class Closure {
    public  String version=""; 

    public static void main(String[] args) {
        Closure c1=new Closure();
        c1.version="1.0";
        Moment m1=c1.getMoment();
        System.out.println(m1.getVersion());
        
        c1.version="2.0";
        Moment m2=c1.getMoment();
        System.out.println(m2.getVersion());
    }

    public Moment getMoment(){
        return new Moment() {
            
            @Override
            public String getVersion() {
                
                return version;
            }
        };
    }

}

interface Moment{
    String getVersion();
}

 

以上分别输出1.0     2.0,说明m1,m2记住了当时的状态,也就是说每次声明Moment对象的时候,编译器会把相关的值拷贝副本,放到对象的私有变量里。
利用这个特性,我们可以实现一些功能:比如一个创建成本很高的对象,需要输出一些包涵自己属性的一些特定对象,这时可以通过闭包这种方式方便的创建所多特定对象,而且每个特定对象可以保持自己的一些独立属性。
 
柯里化
在使用JavaScript的时候,有时会使用科里化,百度百科的释义:
在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。这个技术由 Christopher Strachey 以逻辑学家 Haskell Curry 命名的。
不同于Functor,我认为科里化更侧重于函数的转换和组合。我们可以使用科里化的概念包装一些函数
var calc= function(num){
    return function(y){
        return num + y;
    }
}
var c1= calc (1);
var c2= calc (-1);
其中c1和c2其实是两个不同的函数,通过这种方式我们合理的组合函数
calc(10)(1) 11
calc(0.1)(1) 1.1
而JavaScript动态类型的特点又是得我们可以进行更高级的变换,如:
函数组合
var calc= function(numHandler){
    return function(y){
        return numHandler( y)+1;
    }
}
特定的组合可以简化步骤:
function square(){}
function add(){}
function dvid(){}
function map(handler,list){
}
map(add,list);
map(dvid,list);
map(square,list);
柯里化之后
 
var h=handler(add,dvid,square)
map(h,list)
 
还可以延迟计算:
var curry = function(fn) {
    var _args = []
    return function cb() {
        if (arguments.length == 0) {
            return fn.apply(this, _args)
        }
        Array.prototype.push.apply(_args, arguments);
        return cb;
    }
}
var sum=function(){
     var s=0;
     for (var i = 0;i<arguments.length;i++) {
          s += arguments[i];
     }
     return s;
}
var sumc=curry(sum);
sumc(1)(2)(3)();

java中缺少动态类型,又缺少类似于Prototype的原型,我们只能通过一些其他特性来弥补。
JDK7种我会使用接口和匿名函数来实现动态类和方法,JDK8当然是使用Function接口。先来说JDK7.
JDK7 java实现函数组合。
public class keli {

    public  calculator changeCalc(final initialize init, final int num) {
        return new calculator() {

            @Override
            public int cal(int addend) {
                return init.init(num) + addend;

            }
        };
    }

    public static void main(String[] args) {
        
        initialize init1 = new initialize() {

            @Override
            public int init(int num) {
                num = num * 10;
                return num;
            }
        };
        
        initialize init2 = new init2();
        
        calculator clac1=new keli().changeCalc(init1,3);
        calculator clac2=new keli().changeCalc(init2,3);
        System.out.println(clac1.cal(2));
        System.out.println(clac2.cal(2));
        
    }
}

class init2 implements initialize{

    @Override
    public int init(int num) {
        num = num / 10;
        return num;
    }    
}

interface calculator {
    int cal(int num);
}

interface initialize {
    int init(int num);
}
注意:在JDK7以下需要加final关键字来固定住函数参数(JDK8不用加,编译器内部帮我们加了)。
上面我们通过将匿名函数作为参数传入构造了两个新的函数clac1
和 clac2,他们分别拥有不同的作用域。在实际中一般明确的业务不会这样写,但是处理动态的流程,比如自定义的计算公式,一些插件会用到。
 
JDK8中的默认方法
jdk8实现了默认方法,简而言之就是在街口中添加default关键字,给继承类添加方法,这其实是一个妥协的方案,为了给原有类型增加功能,又不破坏大的结构。类似方式有在C#中我们看到的扩展方法。
今天我们借默认方法实现一些类似Prototype的功能

 

public class currying3 implements Proto {
    public List<String> paramList = null;

    public currying3(List<String> paramList) {
        this.paramList = paramList;
    }

    @Override
    public List<String> getList() {
        
        return this.paramList;
    }
    
    public static void main(String[] args) {
        
        List<String> paramList = new ArrayList<String>();
        Proto p = new currying3(paramList);
        p.handle("aa").handle("bb").handle("cc").handle();
        System.out.println("end.....");

        List<String> paramList1 = new ArrayList<String>();
        Proto p1 = new currying3(paramList1);
        p1.handle("dd").handle("ee").handle("ff").handle();
        System.out.println("end.....");
    }
}

interface Proto {
    public List<String> getList();
    
    default Proto handle(String... param) {
        List<String> paramList=this.getList();
        if (param == null || param.length <= 0) {
            for (String Str : paramList) {
                System.out.println(Str);
            }
            return null;
        } else {

            for (String Str : param) {
                paramList.add(Str);
            }

            return this;
        }
    }
} 
输出结果:
aa
bb
cc
end.....
dd
ee
ff
end.....
当handle参数为空的时候,计算结果,这种方式可以用在可变数量的统计,延迟计算,查询中(不同于Hibernate的延迟查询,Hibernate通过代理类,声明时给予空属性,真正属性求值时在调用查询方法)。
 

java函数式编程--柯里化(Currying),闭包

原文:http://www.cnblogs.com/wanglao/p/6506959.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!