结合记忆和尾递归 [英] Combine memoization and tail-recursion
问题描述
是否有可能以某种方式结合记忆和尾递归?
假设我有以下 memoize
let memoize f = let cache = new Dictionary< _,_>()
(fun x - > match cache.TryGetValue(x)与
| true,y - > y
| _ - >让v = f(x)
cache.Add(x,v)
v)
和以下 factorial
函数:
let rec factorial(x)= if(x = 0)then 1 else x * factorial(x - 1)
记忆 factorial
并不太困难,并且它的尾递归不是要么:
let rec memoized Factorial =
memoize(fun x - > if(x = 0)then 1 else x * memoizedFactorial(x-1))
let tailRecursiveFactorial(x)=
let rec factorialUtil(x,res)= if(x = 0) )
then res
else let newRes = x * res
factorialUtil(x-1,newRes)
factorialUtil(x,1)
但是,您可以结合记忆和尾递归吗?我做了一些尝试,但似乎无法得到它的工作。或者这是不可能的?
与往常一样,延续产生优雅的tailcall解决方案:
打开System.Collections.Generic
let cache = Dictionary< _,_>()// TODO在
内移动let memoizedTRFactorial =
让rec fac nk = //必须使tailcalls到k
匹配cache.TryGetValue(n)with
| true,r - > k r
| _ - >
如果n = 0,那么
k 1
else
fac(n-1)(fun r1 - >
printfn乘以%dn // // * **
让r = r1 * n
cache.Add(n,r)
kr)
fun n - > fac n id
printfn---
let r = memoizedTRFactorial 4
printfn%dr
用于缓存中的KeyValue(k,v)do
printfn%d:%dkv
printfn---
let r2 = memoizedTRFactorial 5
printfn%dr2
printfn---
//注释掉***行,然后运行这个
//让r3 = memoizedTRFactorial 100000
// printfn% dr3
有两种测试。首先,这个调用F(4)的演示可以随意缓存F(4),F(3),F(2),F(1)。 然后,注释掉 ***
printf,并取消注释最终测试(并在Release模式下编译),以表明它不是StackOverflow(它正确使用tailcalls)。 p>
也许我会概括'memoize',然后在'fib'上演示它...
编辑
好吧,下一步,我认为,将memoization与factorial解耦:
open System.Collections.Generic
let cache = Dictionary< _,_>()// TODO在
内移动let memoize fGuts n =
let rec newFunc nk = //必须使$ tail $ k
匹配cache.TryGetValue(n)with
| true,r - > k r
| _ - >
fGuts n(fun r - >
cache.Add(n,r)
kr)newFunc
newFunc n id
let TRFactorialGuts nk memoGuts =
如果n = 0,那么
k 1
else
memoGuts(n-1)(fun r1 - >
printfn乘以%dn // ***
let r = r1 * n
kr)
let memoizedTRFactorial = memoize TRFactorialGuts
printfn---
let r = memoizedTRFactorial 4
printfn%dr
为缓存中的KeyValue(k,v)做
printfn%d:%dkv
printfn---
让r2 = memoizedTRFactorial 5
printfn%dr2
printfn---
//注释掉***行,然后运行这个
//让r3 = memoizedTRFactorial 100000
// printfn%dr3
编辑
好的,这是一个完全一般化的版本,似乎有效。
打开System.Collections.Generic
let memoize fGuts =
let cache = Dictionary< _,_>()
let rec newFunc nk = //必须使tailcalls为k
match cache.TryGetValue n)与
| true,r - > k r
| _ - >
fGuts n(fun r - >
cache.Add(n,r)
kr)newFunc
cache,(fun n - > newFunc n id)
让TRFactorialGuts nk memoGuts =
如果n = 0那么
k 1
else
memoGuts(n-1)(fun r1 - >
printfn乘以%d n // ***
让r = r1 * n
kr)
让facCache,memoizedTRFactorial = memoize TRFactorialGuts
printfn---
let r = memoizedTRFactorial 4
printfn%dr
for facCache中的KeyValue(k,v)do
printfn%d:%dkv
printfn---
let r2 = memoizedTRFactorial 5
printfn%dr2
printfn---
//注释掉***行,然后运行这个
//让r3 = memoizedTRFactorial 100000
// printfn%dr3
让TRFibGuts nk memoGuts =
如果n = 0 || n = 1 then
k 1
else
memoGuts(n-1)(fun r1 - >
memoGuts(n-2)(fun r2 - >
printfn添加%d +%dr1 r2 // %%%
让r = r1 + r2
kr))
let fibCache,memoizedTRFib = memoize TRFibGuts
printfn - -
让r5 = memoizedTRFib 4
printfn%dr5
为fibCache中的KeyValue(k,v)做
printfn%d:%dkv
printfn---
让r6 = memoizedTRFib 5
printfn%dr6
printfn---
//注释掉%%行,然后运行这个
//让r7 = memoizedTRFib 100000
// printfn%dr7
Is it possible to combine memoization and tail-recursion somehow? I'm learning F# at the moment and understand both concepts but can't seem to combine them.
Suppose I have the following memoize
function (from Real-World Functional Programming):
let memoize f = let cache = new Dictionary<_, _>()
(fun x -> match cache.TryGetValue(x) with
| true, y -> y
| _ -> let v = f(x)
cache.Add(x, v)
v)
and the following factorial
function:
let rec factorial(x) = if (x = 0) then 1 else x * factorial(x - 1)
Memoizing factorial
isn't too difficult and making it tail-recursive isn't either:
let rec memoizedFactorial =
memoize (fun x -> if (x = 0) then 1 else x * memoizedFactorial(x - 1))
let tailRecursiveFactorial(x) =
let rec factorialUtil(x, res) = if (x = 0)
then res
else let newRes = x * res
factorialUtil(x - 1, newRes)
factorialUtil(x, 1)
But can you combine memoization and tail-recursion? I made some attempts but can't seem to get it working. Or is this simply not possible?
As always, continuations yield an elegant tailcall solution:
open System.Collections.Generic
let cache = Dictionary<_,_>() // TODO move inside
let memoizedTRFactorial =
let rec fac n k = // must make tailcalls to k
match cache.TryGetValue(n) with
| true, r -> k r
| _ ->
if n=0 then
k 1
else
fac (n-1) (fun r1 ->
printfn "multiplying by %d" n //***
let r = r1 * n
cache.Add(n,r)
k r)
fun n -> fac n id
printfn "---"
let r = memoizedTRFactorial 4
printfn "%d" r
for KeyValue(k,v) in cache do
printfn "%d: %d" k v
printfn "---"
let r2 = memoizedTRFactorial 5
printfn "%d" r2
printfn "---"
// comment out *** line, then run this
//let r3 = memoizedTRFactorial 100000
//printfn "%d" r3
There are two kinds of tests. First, this demos that calling F(4) caches F(4), F(3), F(2), F(1) as you would like.
Then, comment out the ***
printf and uncomment the final test (and compile in Release mode) to show that it does not StackOverflow (it uses tailcalls correctly).
Perhaps I'll generalize out 'memoize' and demonstrate it on 'fib' next...
EDIT
Ok, here's the next step, I think, decoupling memoization from factorial:
open System.Collections.Generic
let cache = Dictionary<_,_>() // TODO move inside
let memoize fGuts n =
let rec newFunc n k = // must make tailcalls to k
match cache.TryGetValue(n) with
| true, r -> k r
| _ ->
fGuts n (fun r ->
cache.Add(n,r)
k r) newFunc
newFunc n id
let TRFactorialGuts n k memoGuts =
if n=0 then
k 1
else
memoGuts (n-1) (fun r1 ->
printfn "multiplying by %d" n //***
let r = r1 * n
k r)
let memoizedTRFactorial = memoize TRFactorialGuts
printfn "---"
let r = memoizedTRFactorial 4
printfn "%d" r
for KeyValue(k,v) in cache do
printfn "%d: %d" k v
printfn "---"
let r2 = memoizedTRFactorial 5
printfn "%d" r2
printfn "---"
// comment out *** line, then run this
//let r3 = memoizedTRFactorial 100000
//printfn "%d" r3
EDIT
Ok, here's a fully generalized version that seems to work.
open System.Collections.Generic
let memoize fGuts =
let cache = Dictionary<_,_>()
let rec newFunc n k = // must make tailcalls to k
match cache.TryGetValue(n) with
| true, r -> k r
| _ ->
fGuts n (fun r ->
cache.Add(n,r)
k r) newFunc
cache, (fun n -> newFunc n id)
let TRFactorialGuts n k memoGuts =
if n=0 then
k 1
else
memoGuts (n-1) (fun r1 ->
printfn "multiplying by %d" n //***
let r = r1 * n
k r)
let facCache,memoizedTRFactorial = memoize TRFactorialGuts
printfn "---"
let r = memoizedTRFactorial 4
printfn "%d" r
for KeyValue(k,v) in facCache do
printfn "%d: %d" k v
printfn "---"
let r2 = memoizedTRFactorial 5
printfn "%d" r2
printfn "---"
// comment out *** line, then run this
//let r3 = memoizedTRFactorial 100000
//printfn "%d" r3
let TRFibGuts n k memoGuts =
if n=0 || n=1 then
k 1
else
memoGuts (n-1) (fun r1 ->
memoGuts (n-2) (fun r2 ->
printfn "adding %d+%d" r1 r2 //%%%
let r = r1+r2
k r))
let fibCache, memoizedTRFib = memoize TRFibGuts
printfn "---"
let r5 = memoizedTRFib 4
printfn "%d" r5
for KeyValue(k,v) in fibCache do
printfn "%d: %d" k v
printfn "---"
let r6 = memoizedTRFib 5
printfn "%d" r6
printfn "---"
// comment out %%% line, then run this
//let r7 = memoizedTRFib 100000
//printfn "%d" r7
这篇关于结合记忆和尾递归的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!