函数的记忆

记忆于函数而言是指对于需要多次调用的值进行缓存的机制。目前来说,函数式的编程语言普遍都支持这种特性。

如果我们反复调用一个函数,增加一个内部缓存可以提高计算的效率,用更多的内存去换取更高的效率。

但是只有纯函数才可以适用缓存技术,纯函数是没有副作用的函数:它没有引用其他值可变的类字段。除了返回值之外不设置其他的变量,其结果完全由参数决定。

缓存

有两种函数缓存的用法,一种是外部调用,一类是内部缓存。以及两种实现方式,一种是手工管理状态,另外一种是记忆机制。

对于程序,可以用一个HashMap来存放已经计算过的值,提高下次计算的速度。

import java.util.HashMap;
import java.util.Map;

public class CacheValue {

    private static Map<Integer,Boolean> result = new HashMap<>();

    public static boolean isPrime(int num) {

        if (result.containsKey(num)){
            return true;
        }

        for(int i = 2; i <= Math.sqrt(num); i++) {
            if(num % i == 0) {
                result.put(num,false);
                return false;
            }
        }

        result.put(num,true);
        return true;
    }
}

这种方式提高了运算速度,但是函数不再由纯粹的静态方法组成,类有了缓存就有了状态。带来了一系列的问题。缓存有代价:它提高了代码的非本质性和维护成本。

引入记忆

函数式语言通常会引入记忆机制来帮助开发者完成。

Java8中要记忆一个函数,需要先将需要记忆的函数定义成闭包,然后对这个闭包执行 获得一个新的函数,以后调用这个新的函数时,结果都会被缓存起来。

由于Java8,暂时在API中没有提供这种记忆机制,这个函数需要自己实现。

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

public class Memoizer<T, U> {

    private final Map<T, U> cache = new ConcurrentHashMap<>();

    private Memoizer() {}
    private Function<T, U> doMemoize(final Function<T, U> function) {
        return input -> cache.computeIfAbsent(input, function::apply);
    }

    public static <T, U> Function<T, U> memoize(final Function<T, U> function) {
        //每个实例都会有独立的cache
        return new Memoizer<T, U>().doMemoize(function);
    }
}

原谅我写的这个count()函数还是不够单纯。。。

public class Memoization {

    Function<Integer,Integer> f = this::count;
    Function<Integer,Integer> g = Memoizer.memoize(f);

    public Integer count(int num){
        int sum =0;
        for (int i = 0; i < num; i++) {
            sum += i;
        }
        return sum;
    }

    public void test(){

        long start = System.currentTimeMillis();
        Integer flag = g.apply(99999999);
        System.out.println(flag);
        System.out.println(System.currentTimeMillis()-start);
        start = System.currentTimeMillis();
        flag = g.apply(99999999);
        System.out.println(flag);
        System.out.println(System.currentTimeMillis()-start);

    }

    public static void main(String args[]){
        Memoization memoization = new Memoization();
        memoization.test();
    }

}

这种记忆一个函数的方式是在操作函数本身,而非函数的结果。而且被记忆的内容该是值不可变的。

所以所有被记忆的函数应该是:

  • 没有副作用的
  • 不依赖任何外部环境。
posted @ 2015-08-31 19:26  whthomas  阅读(713)  评论(0编辑  收藏  举报