Кеширование с использованием функциональных обратных вызовов / прокси-шаблонов реализации 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

 }

3 ответа

Решение

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

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

Этот ответ имеет хорошее объяснение разницы между def а также val, /questions/47811212/razlichiya-mezhdu-etimi-tremya-sposobami-opredeleniya-funktsii-v-scala/47811220#47811220

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

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

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

например

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

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

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

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

Вот модификация вашего кода, которая следует этому подходу. 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
Другие вопросы по тегам