此代码来自一篇名为"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
答案 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#解决方案看起来可能与上面的相似。