Ricerca…
Memoizzazione semplice
La Memoizzazione consiste nei risultati della funzione di memorizzazione nella cache per evitare di calcolare lo stesso risultato più volte. Ciò è utile quando si lavora con funzioni che eseguono calcoli costosi.
Possiamo usare una semplice funzione fattoriale come esempio:
let factorial index =
let rec innerLoop i acc =
match i with
| 0 | 1 -> acc
| _ -> innerLoop (i - 1) (acc * i)
innerLoop index 1
Chiamando questa funzione più volte con lo stesso parametro si ottiene sempre lo stesso calcolo.
La Memoizzazione ci aiuterà a memorizzare nella cache il risultato fattoriale e a restituirlo se lo stesso parametro viene passato di nuovo.
Ecco una semplice implementazione di memoization:
// memoization takes a function as a parameter
// This function will be called every time a value is not in the cache
let memoization f =
// The dictionary is used to store values for every parameter that has been seen
let cache = Dictionary<_,_>()
fun c ->
let exist, value = cache.TryGetValue (c)
match exist with
| true ->
// Return the cached result directly, no method call
printfn "%O -> In cache" c
value
| _ ->
// Function call is required first followed by caching the result for next call with the same parameters
printfn "%O -> Not in cache, calling function..." c
let value = f c
cache.Add (c, value)
value
La funzione di memoization
prende semplicemente una funzione come parametro e restituisce una funzione con la stessa firma. Si può vedere questo nel metodo signature f:('a -> 'b) -> ('a -> 'b)
. In questo modo puoi usare la memoizzazione come se stessi chiamando il metodo fattoriale.
Le chiamate printfn
servono a mostrare cosa succede quando chiamiamo la funzione più volte; possono essere rimossi in modo sicuro.
Usare la memoizzazione è facile:
// Pass the function we want to cache as a parameter via partial application
let factorialMem = memoization factorial
// Then call the memoized function
printfn "%i" (factorialMem 10)
printfn "%i" (factorialMem 10)
printfn "%i" (factorialMem 10)
printfn "%i" (factorialMem 4)
printfn "%i" (factorialMem 4)
// Prints
// 10 -> Not in cache, calling function...
// 3628800
// 10 -> In cache
// 3628800
// 10 -> In cache
// 3628800
// 4 -> Not in cache, calling function...
// 24
// 4 -> In cache
// 24
Memoizzazione in una funzione ricorsiva
Usando l'esempio precedente di calcolare il fattoriale di un intero, inserire nella tabella hash tutti i valori fattoriali calcolati all'interno della ricorsione, che non appaiono nella tabella.
Come nell'articolo sulla memoizzazione , dichiariamo una funzione f
che accetta un fact
parametro funzione e un parametro intero. Questa funzione f
include istruzioni per calcolare il fattoriale di n
da fact(n-1)
.
Ciò consente di gestire la ricorsione mediante la funzione restituita da memorec
e non dal fact
stesso ed eventualmente interrompere il calcolo se il valore dei fact(n-1)
è già stato calcolato e si trova nella tabella hash.
let memorec f =
let cache = Dictionary<_,_>()
let rec frec n =
let value = ref 0
let exist = cache.TryGetValue(n, value)
match exist with
| true ->
printfn "%O -> In cache" n
| false ->
printfn "%O -> Not in cache, calling function..." n
value := f frec n
cache.Add(n, !value)
!value
in frec
let f = fun fact n -> if n<2 then 1 else n*fact(n-1)
[<EntryPoint>]
let main argv =
let g = memorec(f)
printfn "%A" (g 10)
printfn "%A" "---------------------"
printfn "%A" (g 5)
Console.ReadLine()
0
Risultato:
10 -> Not in cache, calling function...
9 -> Not in cache, calling function...
8 -> Not in cache, calling function...
7 -> Not in cache, calling function...
6 -> Not in cache, calling function...
5 -> Not in cache, calling function...
4 -> Not in cache, calling function...
3 -> Not in cache, calling function...
2 -> Not in cache, calling function...
1 -> Not in cache, calling function...
3628800
"---------------------"
5 -> In cache
120