【问题标题】:Java: automatic memoizationJava:自动记忆
【发布时间】:2011-04-25 11:42:51
【问题描述】:

我的代码中有一些函数,使用 memoization 很有意义(似乎甚至是强制性的)。

我不想为每个函数单独手动实现。有什么方法(例如like in Python)我可以使用注释或做其他事情,以便在我想要的那些功能上自动获得它?

【问题讨论】:

    标签: java python annotations decorator memoization


    【解决方案1】:

    Cyclops 为函数、供应商、可调用对象、谓词和扩展方法提供记忆(通过方法引用)(see javadoc)

    例如

    给定一个变量来计算我们的方法被实际调用的次数,我们可以看到被记忆的函数实际上只执行了一次方法。

    int called = 0;
    
    cached = Memoise.memoiseQuadFunction(this::addAll);
    
    assertThat(cached.apply(1,2,3,4),equalTo(10));
    assertThat(cached.apply(1,2,3,4),equalTo(10));
    assertThat(called,equalTo(1));
    
    private int addAll(int a,int b,int c, int d){
        called++;
        return a+b+c+d;
    }
    

    【讨论】:

      【解决方案2】:

      我遇到了一个名为 Tek271 的记忆库,它似乎使用注释来记忆你描述的函数。

      【讨论】:

      • 啊,我明白了。似乎该库提供了一种为您的对象创建包装器对象的方法,它会自动记住那些通过注释标记为要记忆的函数。
      • 页面已经移动,现在可以在tek271.com/software/java/memoizer找到
      【解决方案3】:

      Spring 3.1 现在提供了一个@Cacheable annotation,它正是这样做的。

      顾名思义,@Cacheable 用于划分可缓存的方法——即将结果存储到缓存中的方法,因此在后续调用(使用相同的参数)时,将返回缓存中的值无需实际执行该方法。

      【讨论】:

        【解决方案4】:

        您可以使用 Google 的 guava 库中的 Function 接口轻松实现您的目标:

        import java.util.HashMap;
        import java.util.Map;
        
        import com.google.common.base.Function;
        
        public class MemoizerTest {
          /**
           * Memoizer takes a function as input, and returns a memoized version of the same function.
           * 
           * @param <F>
           *          the input type of the function
           * @param <T>
           *          the output type of the function
           * @param inputFunction
           *          the input function to be memoized
           * @return the new memoized function
           */
          public static <F, T> Function<F, T> memoize(final Function<F, T> inputFunction) {
            return new Function<F, T>() {
              // Holds previous results
              Map<F, T> memoization = new HashMap<F, T>();
        
              @Override
              public T apply(final F input) {
                // Check for previous results
                if (!memoization.containsKey(input)) {
                  // None exists, so compute and store a new one
                  memoization.put(input, inputFunction.apply(input));
                }
        
                // At this point a result is guaranteed in the memoization
                return memoization.get(input);
              }
            };
          }
        
          public static void main(final String[] args) {
            // Define a function (i.e. inplement apply)
            final Function<Integer, Integer> add2 = new Function<Integer, Integer>() {
              @Override
              public Integer apply(final Integer input) {
                System.out.println("Adding 2 to: " + input);
                return input + 2;
              }
            };
        
            // Memoize the function
            final Function<Integer, Integer> memoizedAdd2 = MemoizerTest.memoize(add2);
        
            // Exercise the memoized function
            System.out.println(memoizedAdd2.apply(1));
            System.out.println(memoizedAdd2.apply(2));
            System.out.println(memoizedAdd2.apply(3));
            System.out.println(memoizedAdd2.apply(2));
            System.out.println(memoizedAdd2.apply(4));
            System.out.println(memoizedAdd2.apply(1));
          }
        }
        

        应该打印:

        将 2 加到:1

        3

        将 2 加到:2

        4

        将 2 添加到:3

        5

        4

        将 2 加到:4

        6

        3

        可以看到第二次 memoizedAdd2 被调用(应用)到参数 2 和 1,apply 中的计算实际上并没有运行,它只是获取存储的结果。

        【讨论】:

        • 更接近我想要的,但仍然过于具体。是否可以进一步概括这一点,以便它可以采用任意数量的参数(而不仅仅是一个)?
        • Guava 的 Function 类将所有输入压缩到一个参数中。现在,该参数的类型可以是 Object[],有效地允许任何内容,但会降低类型检查的有效性。或者,创建一个由 为 2 个参数、一个 Function3 等生成的新 Function2 接口将非常简单。
        • 在 Guava 中,Suppliers 类具有内置的 memoize 和 memoizeWithExpiration 方法。
        【解决方案5】:

        我认为记忆化没有语言原生实现。

        但是你可以很容易地实现它,作为你方法的装饰器。你必须维护一个 Map:你的 Map 的关键是参数,值是结果。

        这是一个简单的实现,用于单参数方法:

        Map<Integer, Integer> memoizator = new HashMap<Integer, Integer>();
        
        public Integer memoizedMethod(Integer param) {
        
            if (!memoizator.containsKey(param)) {
                memoizator.put(param, method(param));
            } 
        
            return memoizator.get(param);
        }
        

        【讨论】:

        • 我怎样才能以一般的方式实现它作为我的方法的装饰器?
        • @Albert:正如 Benoit 所说,没有本机实现(即,如果没有 Java hacking,您无法以一般方式执行此操作),因为 python 装饰器使用了一些“元信息”关于功能。 IE。在 python 中可以让装饰器改变原始函数。这是 - 据我所知 - 在 Java 中是不可能的。
        • “你可以很容易地实现它,作为你方法的装饰器。”
        • @Albert,他的意思是装饰器,更通用的术语是另一个函数的包装器,而不是使它看起来干净和简单的 Python 语法方式。你也许可以通过某种方式用 AspectJ 得到你想要的,但我个人对它还不够熟悉,而且我仍然认为它不会像使用 python 那样容易。
        猜你喜欢
        • 1970-01-01
        • 2021-06-16
        • 2012-01-03
        • 2015-02-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多