结合记忆和尾递归 [英] Combine memoization and tail-recursion

查看:139
本文介绍了结合记忆和尾递归的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否有可能以某种方式结合记忆和尾递归?



假设我有以下 memoize

code>函数(来自真实世界的函数式编程):

  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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆