2016-04-12 3 views
2

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

Несколько дней назад я наткнулся на обратные вызовы и прокси-образов реализации с помощью Scala. Этот код должен применять только внутреннюю функцию, если значение отсутствует на карте. Но каждый раз, когда карта инициализируется и ценности ушли (что кажется obivous.кэша с помощью функциональных обратных вызовов/прокси-модель реализации 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 

} 
+0

Put 'cache' вне функции. – Dima

ответ

1

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

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

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

1

Вы не объявляя новую 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] = { 
+0

Поле, которое является изменчивой коллекцией внутри метода. ** - это хорошая идея для распределенной программы, такой как искра ** –

+0

Бит путают. Вы не можете объявить поле внутри метода –

0

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

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

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

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