кеширование с использованием функциональных обратных вызовов / реализация шаблона прокси scala

Как реализовать кеш с помощью функционального программирования

Несколько дней назад я наткнулся на обратные вызовы и реализацию шаблона прокси с помощью scala. Этот код должен применять внутреннюю функцию только в том случае, если значение отсутствует на карте. Но каждый раз, когда карта переинициализируется и значения исчезают (что кажется очевидным.

Как снова и снова использовать один и тот же кеш между вызовами разных функций

class Aggregator{
  def memoize(function: Function[Int, Int] ):Function[Int,Int] = {
    val cache = HashMap[Int, Int]()
     (t:Int) => {
      if (!cache.contains(t)) {
        println("Evaluating..."+t)
        val r = function.apply(t);
        cache.put(t,r)
        r
      }
       else
      {
        cache.get(t).get;
      }
    }
  }

  def memoizedDoubler = memoize( (key:Int) => {
    println("Evaluating...")
    key*2
    })
  }

object Aggregator {

  def main( args: Array[String] ) {
    val agg = new Aggregator()
    agg.memoizedDoubler(2)
    agg.memoizedDoubler(2)// It should not evaluate again but does
    agg.memoizedDoubler(3)
    agg.memoizedDoubler(3)// It should not evaluate again but does

 }

person Pushpendra Jaiswal    schedule 12.04.2016    source источник
comment
Поместите cache вне функции.   -  person Dima    schedule 12.04.2016


Ответы (3)


Я вижу, что вы здесь пытаетесь сделать, причина того, что это не работает, в том, что каждый раз, когда вы вызываете memoizedDoubler, он сначала вызывает memorize. Вам нужно объявить memoizedDoubler как val вместо def, если вы хотите, чтобы он вызывал memoize только один раз.

  val memoizedDoubler = memoize( (key:Int) => {
    println("Evaluating...")
    key*2
  })

В этом ответе есть хорошее объяснение разницы между def и val. https://stackoverflow.com/a/12856386/37309

person Chad    schedule 20.04.2016

Разве вы не объявляете новый Map на вызов?

def memoize(function: Function[Int, Int] ):Function[Int,Int] = {
    val cache = HashMap[Int, Int]()

вместо того, чтобы указывать по одному на каждый экземпляр Aggregator?

e.g.

class Aggregator{
  private val cache = HashMap[Int, Int]()
  def memoize(function: Function[Int, Int] ):Function[Int,Int] = {
person Brian Agnew    schedule 12.04.2016
comment
Поле, которое представляет собой изменяемую коллекцию внутри метода. это хорошая идея для распределенной программы, такой как Spark - person Pushpendra Jaiswal; 12.04.2016
comment
Немного запутался. Вы не можете объявить поле в методе - person Brian Agnew; 12.04.2016

Чтобы ответить на ваш вопрос:

Как реализовать кеш с помощью функционального программирования

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

Вот модификация вашего кода, которая следует этому подходу. function для расчета значений, а cache включается в Aggregator. Когда вызывается memoize, он возвращает кортеж, содержащий результат вычисления (возможно, взятый из кеша) и новый Aggregator, который следует использовать для следующего вызова.

class Aggregator(function: Function[Int, Int], cache:Map[Int, Int] = Map.empty) {

  def memoize:Int => (Int, Aggregator) = {
    t:Int =>
      cache.get(t).map {
        res =>
          (res, Aggregator.this)
      }.getOrElse {
        val res = function(t)
        (res, new Aggregator(function, cache + (t -> res)))
      }
  }
}

object Aggregator {

  def memoizedDoubler = new Aggregator((key:Int) => {
    println("Evaluating..." + key)
    key*2
  })


  def main(args: Array[String]) {
    val (res, doubler1)  = memoizedDoubler.memoize(2)
    val (res1, doubler2)  = doubler1.memoize(2)
    val (res2, doubler3)  = doubler2.memoize(3)
    val (res3, doubler4)  = doubler3.memoize(3)
  }
}

Это печатает:

Evaluating...2
Evaluating...3
person Aivean    schedule 21.04.2016