Java: автоматическая memoization

У меня есть несколько функций в моем коде, где имеет смысл (кажется, даже обязательно) использовать memoization.

Я не хочу реализовывать это вручную для каждой функции отдельно. Есть ли способ (например, как в Python) Я могу просто использовать аннотацию или сделать что-то еще, поэтому я получаю это автоматически на тех функциях, где я хотите?

Ответы

Ответ 1

Spring 3.1 теперь предоставляет аннотацию @Cacheable, которая делает именно это.

Как следует из названия, @Cacheable используется для демаркации методов, которые можно кэшировать, то есть методов, для которых результат хранится в кеше, а затем при последующих вызовах (с теми же аргументами) возвращается значение в кеше без фактического выполнения метода.

Ответ 2

Я столкнулся с библиотекой memoization под названием Tek271, которая, как вы описываете, использует аннотации для memoize-функций.

Ответ 3

Я не думаю, что существует языковая реализация memoization.

Но вы можете легко реализовать его, как декоратор вашего метода. Вы должны сохранить карту: ключ вашей карты - это параметр, значение результата.

Вот простая реализация для метода с одним аргументом:

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);
}

Ответ 4

Вы можете использовать Function интерфейс в Google guava, чтобы легко достичь того, что вам нужно:

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, вычисление в приложении не выполняется, оно просто извлекает сохраненные результаты.

Ответ 5

Cyclops предлагает информацию о функциях, поставщиках, вызываемых сообщениях, предикатах и ​​методах расширения (через ссылки на методы) (см. javadoc)

например.

Учитывая переменную, которая подсчитывает количество времени, которое фактически вызывается нашим методом, мы можем видеть, что memoised функция фактически выполняет метод только один раз.

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;
}