在上一个问题中,我被告知如何重写我的计算表达式,以便它使用尾递归。我重写了我的代码,但仍然得到了 StackOverflowException。为了找到问题,我使用 state monad 编写了一些小代码(取自此博客条目):
type State<'a, 's> = State of ('s -> 'a * 's)
let runState (State s) initialState = s initialState
let getState = State (fun s -> (s,s))
let putState s = State (fun _ -> ((),s))
type StateBuilder() =
member this.Return a = State (fun s -> (a, s))
member this.Bind(m, k) =
State (fun s -> let (a,s') = runState m s in runState (k a) s')
member this.ReturnFrom a = a
let state = new StateBuilder()
let s max =
let rec Loop acc = state {
let! n = getState
do! putState (n + 1)
if acc < max then
return! Loop (acc + 1)
else return acc
}
Loop 0
runState (s 100000) 0
这再次抛出 StackOverflowException,尽管 Loop 函数可以使用尾递归(?)。我猜 StateBuilder 类有问题。我试图用延迟方法做一些事情。将所有内容包装在一个额外的 lambda 中,但没有成功。我现在完全卡住了。这是我的第二次尝试(不编译):
type State<'a, 's> = State of ('s -> 'a * 's)
let runState (State s) initialState = s initialState
let getState = fun () -> State (fun s -> (s,s))
let putState s = fun () -> State (fun _ -> ((),s))
type StateBuilder() =
member this.Delay(f) = fun () -> f()
member this.Return a = State (fun s -> (a, s))
member this.Bind(m, k) =
fun () -> State (fun s -> let (a,s') = runState (m ()) s in runState ((k a) ()) s')
member this.ReturnFrom a = a
let state = new StateBuilder()
let s max =
let rec Loop acc = state {
let! n = getState
do! putState (n + 1 - acc)
if acc < max then
return! Loop (acc + 2)
else return acc
}
Loop 0
runState (s 100000 ()) 0