首页 > 解决方案 > Prefix_action, suffix_action 带序列

问题描述

我想用 seq (resp suffix_action) 编写一个函数 prefix_action,这是 BatEnum 中的代码:

 let prefix_action f t =
  let full_action e =
    e.count <- (fun () -> t.count());
    e.next  <- (fun () -> t.next ());
    e.clone <- (fun () -> t.clone());
    f ()
  in
  let rec t' =
    {
      count = (fun () -> full_action t'; t.count() );
      next  = (fun () -> full_action t'; t.next()  );
      clone = (fun () -> full_action t'; t.clone() );
      fast  = t.fast
    } in t'

我想知道,因为我们没有序列中的克隆,我想知道在这些情况下我应该如何考虑克隆(它是对序列的使用),如果是这种情况,我们如何才能获得使用顺序? Prefix_action 文档

标签: ocaml

解决方案


定义的序列没有克隆功能,只是因为它是“默认定义的”。

type 'a node =
|   Nil
|   Cons of 'a * 'a t
and 'a t = unit -> 'a node

正如你所看到的,它只是一个返回一些 sum 类型的函数,如果你愿意,可以是简单的值,没有副作用(实际上它们可以隐藏在函数体中,但现在让我欺骗你)。因此,这种情况下的克隆函数只是一个身份:

let clone s = s

现在,如果您查看枚举的定义,您会注意到很少的mutable关键字:

type 'a t = { 
  mutable count : unit -> int;
  mutable next  : unit -> 'a;
  mutable clone : unit -> 'a t;
  mutable fast  : bool;
}

如果我们尝试使用与clone序列相同的方法,我们会注意到一个副本的更改会影响另一个副本:

# let e1 = { fast = true; (* ... *) };;
val e1 : 'a t = {fast = true; (* ... *)}
# let e2 = clone e1;;
val e2 : 'a t = {fast = true; (* ... *)}
# e1.fast <- false;;
- : unit = ()
# e2;;
'a t = {fast = false; (* ... *)}

这就是为什么他们需要clone功能。

所以现在您可以实现您的功能,例如prefix_action.

prefix_action f e将表现为e但保证将在读取f ()当前第一个元素之前仅调用一次。e

问题出在这个“恰好一次”上。我不确定它是什么意思,但是可以说这意味着如果您将序列传递给prefix_action f然后两次传递给hd,那么f将只执行一次(因为如果它意味着不同的东西,那就没意思了)。现在我们可以回到这个“副作用”的故事。显然,没有它们我们就无法实施prefix_action。序列的类型不包含任何mutable关键字,但它包含函数!因此,我们可以将副作用包装到函数中。

let prefix_action : (unit -> unit) -> 'a t -> 'a t = fun f s ->
    let b = ref true in
    fun () -> (if !b then f (); b := false); s ()

但是现在,由于我们有副作用,我们需要重新定义clone. 从规格prefix_action

Ifprefix_action f e被克隆,f在克隆期间仅被调用一次。

因此我们的clone

let clone s = let _ = s (); s

推荐阅读