【问题标题】:Simple Generators简单的生成器
【发布时间】:2012-10-30 19:16:16
【问题描述】:

此代码来自一篇名为"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

【问题讨论】:

  • 好的,所以例子可以在论文链接的repo中找到。

标签: haskell f# ocaml yield iterate


【解决方案1】:

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

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

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

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

【讨论】:

    【解决方案2】:

    我认为 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# 解决方案可能看起来与上面的非常相似。

    【讨论】:

    • 托马斯,谢谢你分享这个。这回答了一个我还没有问的问题。我认为论文中声称的主要优势是实现的速度和简单性(基于类型表示)。我只是对代码的工作方式感到好奇。再次感谢。
    猜你喜欢
    • 2012-08-20
    • 1970-01-01
    • 1970-01-01
    • 2013-03-20
    • 2010-10-08
    • 1970-01-01
    • 1970-01-01
    • 2011-11-27
    • 1970-01-01
    相关资源
    最近更新 更多