【发布时间】:2015-03-20 16:08:31
【问题描述】:
我看到的典型 Pause monad 实现如下所示(基于 Giulia Costantini 和 Giuseppe Maggiore 的 Friendly F# 中的第 5 章)。
open System
type Process<'a> = unit -> 'a Step
and Step<'a> =
| Continue of 'a
| Paused of 'a Process
type PauseMonad () =
member this.Return x = fun () -> Continue x
member this.ReturnFrom x = x
member this.Bind (result, rest) =
fun () ->
match result () with
| Continue x -> rest x ()
| Paused p -> Paused (this.Bind (p, rest))
let yield_ () =
fun () ->
Paused (fun () ->
Continue ())
let get_process_step process_ step = do printfn "Process %d, step %d." process_ step
let get_last_process_step process_ = do printfn "Process %d finished." process_
let rec get_process process_ step_count =
PauseMonad () {
do! yield_ ()
if step_count = 0 then
do get_last_process_step process_
return ()
else
do get_process_step process_ step_count
return! get_process process_ <| step_count - 1
}
let rec race p1 p2 =
match p1 (), p2 () with
| Continue _, _ -> do printfn "Process 1 finished first."
| _, Continue _ -> do printfn "Process 2 finished first."
| Paused p1_, Paused p2_ -> race (p1_) (p2_)
[<EntryPoint>]
let main _ =
let process_1 = get_process 1 5
let process_2 = get_process 2 7
do race process_1 process_2
0
Here 是 Haskell 中的类似实现。
但是,摆脱相互递归的类型 Process 和 Step 似乎更简单,而只使用单个递归类型 Process,如下所示。
open System
type Process<'a> =
| Continue of 'a
| Paused of (unit -> 'a Process)
type PauseMonad () =
member this.Return x = Continue x
member this.ReturnFrom x = x
member this.Bind (result, rest) =
match result with
| Continue x -> Paused (fun () -> rest x)
| Paused p -> Paused (fun () -> this.Bind (p (), rest))
let yield_ () =
Paused (fun () ->
Continue ())
let get_process_step process_ step = do printfn "Process %d, step %d." process_ step
let get_last_process_step process_ = do printfn "Process %d finished." process_
let rec get_process process_ step_count =
PauseMonad () {
do! yield_ ()
if step_count = 0 then
do get_last_process_step process_
return ()
else
do get_process_step process_ step_count
return! get_process process_ <| step_count - 1
}
let rec race p1 p2 =
match p1, p2 with
| Continue _, _ -> do printfn "Process 1 finished first."
| _, Continue _ -> do printfn "Process 2 finished first."
| Paused p1_, Paused p2_ -> race (p1_ ()) (p2_ ())
[<EntryPoint>]
let main _ =
let process_1 = get_process 1 5
let process_2 = get_process 2 7
do race process_1 process_2
0
这些实现中的任何一个都给了我相同的输出:
Process 1, step 5.
Process 2, step 7.
Process 1, step 4.
Process 2, step 6.
Process 1, step 3.
Process 2, step 5.
Process 1, step 2.
Process 2, step 4.
Process 1, step 1.
Process 2, step 3.
Process 1 finished.
Process 2, step 2.
Process 1 finished first.
为了便于区分,我已使这两种实现尽可能相似。据我所知,唯一的区别是:
在第一个版本中,yield_、PauseMonad.Return 和 PauseMonad.Bind 为返回值添加了延迟。在第二个版本中,PauseMonad.Return 在 Paused 包装器中添加了延迟。
在第一个版本中,PauseMonad.Bind 运行结果过程的一个步骤以查看返回值是否匹配 Continue 或 Paused。在第二个版本中,PauseMonad.Bind 仅在确定与 Paused 匹配后才运行结果过程的一个步骤。
在第一个版本中,race 在每个进程中运行一个步骤,检查两个结果是否与 Paused 匹配,并与剩余进程递归。在第二个版本中,race 检查两个进程是否匹配 Paused,然后运行每个进程的一个步骤,并使用这些步骤的返回值进行递归。
第一个版本更好有什么原因吗?
【问题讨论】: