Dav*_*ier 10 monads f# computation-expression
使用以下continuation monad:
type ContinuationMonad() =
member this.Bind (m, f) = fun c -> m (fun a -> f a c)
member this.Return x = fun k -> k x
let cont = ContinuationMonad()
Run Code Online (Sandbox Code Playgroud)
我不明白为什么以下给我一个堆栈溢出:
let map f xs =
let rec map xs =
cont {
match xs with
| [] -> return []
| x :: xs ->
let! xs = map xs
return f x :: xs
}
map xs id;;
let q = [1..100000] |> map ((+) 1)
Run Code Online (Sandbox Code Playgroud)
虽然以下不是:
let map f xs =
let rec map xs =
cont {
match xs with
| [] -> return []
| x :: xs ->
let! v = fun g -> g(f x)
let! xs = map xs
return v :: xs
}
map xs id;;
let q = [1..100000] |> map ((+) 1)
Run Code Online (Sandbox Code Playgroud)
要修复您的示例,请将此方法添加到monad的定义中:
member this.Delay(mk) = fun c -> mk () c
Run Code Online (Sandbox Code Playgroud)
显然,溢出的部分是在递归调用中破坏大输入列表map.延迟它解决了这个问题.
需要注意的是你的第二个版本提出的递归调用map背后另有let!其desugars到Bind和一个额外的拉姆达,实际上推迟到递归调用map.
在得出这个结论之前,我不得不追求一些虚假的痕迹.除非递归呼叫被延迟,否则观察到的StackOverflow情况OCaml也是如此(尽管处于更高的位置N).虽然F#TCO有一些怪癖,但OCaml更为成熟,所以这使我确信问题确实存在于代码中,而不是编译器:
let cReturn x = fun k -> k x
let cBind m f = fun c -> m (fun a -> f a c)
let map f xs =
(* inner map loop overflows trying to pattern-match long lists *)
let rec map xs =
match xs with
| [] -> cReturn []
| x :: xs ->
cBind (map xs) (fun xs -> cReturn (f x :: xs)) in
map xs (fun x -> x)
let map_fixed f xs =
(* works without overflowing by delaying the recursive call *)
let rec map xs =
match xs with
| [] -> cReturn []
| x :: xs ->
cBind (fun c -> map xs c) (fun xs -> cReturn (f x :: xs)) in
map xs (fun x -> x)
let map_fused f xs =
(* manually fused version avoids the problem by tail-calling `map` *)
let rec map xs k =
match xs with
| [] -> k []
| x :: xs ->
map xs (fun xs -> k (f x :: xs)) in
map xs (fun x -> x)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
827 次 |
| 最近记录: |