简单的发电机

时间:2012-10-30 19:16:16

标签: haskell f# ocaml yield iterate

此代码来自一篇名为"Lazy v. Yield"的论文。它是一种解耦数据流的生产者和消费者的方法。我理解代码的Haskell部分,但O'Caml / F#让我不知所措。由于以下原因,我不理解此代码:

对于将参数作为参数作为异常并返回单位的函数,我可以期待什么样的行为?

消费者如何投射到特定的例外? (这是什么意思?)

消费者的例子是什么?

module SimpleGenerators

type 'a gen        = unit -> 'a
type producer      = unit gen
type consumer      = exn -> unit  (* consumer will project into specific exception *)
type 'a transducer = 'a gen -> 'a gen 

let yield_handler : (exn -> unit) ref = 
    ref (fun _ -> failwith "yield handler is not set")

let iterate (gen : producer) (consumer : consumer) : unit = 
    let oldh = !yield_handler in
    let rec newh x =
        try 
            yield_handler := oldh
            consumer x
            yield_handler := newh
        with e -> yield_handler := newh; raise e
    in
    try
        yield_handler := newh
        let r = gen () in
        yield_handler := oldh
        r
    with e -> yield_handler := oldh; raise e

2 个答案:

答案 0 :(得分:4)

我不熟悉这篇论文,所以其他人可能会更有启发性。在此期间,这里有一些快速的答案/猜测。

类型exn -> unit的函数基本上是一个异常处理程序。

例外可以包含数据。它们与多态变体非常相似 - 即,您可以随时添加新的异常,并且它可以充当数据构造函数。

消费者看起来会寻找能够为其提供所需数据的特定异常。其他人只会重新加注。所以,它只关注可能异常空间的投影(我猜)。

答案 1 :(得分:3)

我认为OCaml示例使用了一些您通常不会在F#中使用的构造和设计模式,因此它非常适合OCaml。正如Jeffrey所说,OCaml程序经常使用控制流的异常(而在F#中,它们仅用于异常情况)。

此外,F#具有非常强大的序列表达式机制,可以非常好地用于将数据生成器与数据使用者分开。我没有详细阅读论文,所以也许他们有更复杂的东西,但F#中的一个简单例子可能如下所示:

// Generator: Produces infinite sequence of numbers from 'start'
// and prints the numbers as they are being generated (to show I/O behaviour)
let rec numbers start = seq { 
  printfn "generating: %d" start
  yield start
  yield! numbers (start + 1) }

可以使用for循环实现简单的使用者,但如果我们想要使用流,我们需要说明使用Seq.take消耗多少元素:

// Consumer: takes a sequence of numbers generated by the 
// producer and consumes first 100 elements 
let consumer nums = 
  for n in nums |> Seq.take 100 do
    printfn "consuming: %d" n

当您运行consumer (numbers 0)代码时,代码开始打印:

generating: 0
consuming: 0
generating: 1
consuming: 1
generating: 2
consuming: 2

所以你可以看到生产者和消费者的影响是交错的。我觉得这很简单&强大的机制,但也许我错过了论文的重点,他们有更有趣的东西。如果是这样,请告诉我!虽然我认为惯用的F#解决方案看起来可能与上面的相似。