如何在Ocaml中实现俄罗斯玩偶模式?

如何在Ocaml中实现俄罗斯玩偶模式?,ocaml,Ocaml,在Javascript中有一种模式称为俄罗斯玩偶模式(这也可以称为“一次性”)。基本上,它是一个在某个时刻用另一个函数替换自身的函数 简单的例子: var func = function(){ func = function(){ console.log("subsequent calls call this...");}; console.log("first call"); } 因此,第一次调用func时,它将输出“first call”,下一次(以及后续时间)它将打印“后续调用

在Javascript中有一种模式称为俄罗斯玩偶模式(这也可以称为“一次性”)。基本上,它是一个在某个时刻用另一个函数替换自身的函数

简单的例子:

var func = function(){ 
  func = function(){ console.log("subsequent calls call this...");};
  console.log("first call");
}
因此,第一次调用func时,它将输出“first call”,下一次(以及后续时间)它将打印“后续调用调用此…”。(例如,这在Scheme中也很容易做到)

我一直在琢磨如何在Ocaml中实现这一点

编辑:我想出了一个解决方案:

 let rec func = ref( fun () -> func := ( fun () -> Printf.printf("subsequent..\n"));Printf.printf("First..\n"));;
称为: !func()


有趣的是,如果我不在定义中包含'rec',它永远不会调用后续函数。。。它总是打印“第一…”。

这很简单,但你需要使用副作用。这里有一个函数,它接受两个thunk作为参数,并返回一个新的thunk,第一次调用第一个thunk,每隔一次调用第二个thunk

let doll f1 f2 =
   let f = ref f1 in
   (fun () ->
      let g = !f in
      f := f2;
      g ())
这不是很理想,因为我们会不断地用相同的值覆盖ref

这里有一个稍微好一点的版本,它使用递归定义

let doll f1 f2 =
   let rec f = ref (fun () -> f := f2;f1 ()) in
   (fun () -> !f ())
那么,现在,您将看到:

# let f = doll (fun () -> 1) (fun () -> 2);;
val f : unit -> int = <fun>
# f ();;
- : int = 1
# f ();;
- : int = 2
#让f=doll(fun()->1)(fun()->2);;
val f:单位->整数=
#f();;
-:int=1
#f();;
-:int=2

YZLR的答案很好,但有两点需要注意:

它强制函数的输入为unit类型。您可以使用多态版本:

let doll f1 f2 =
  let rec f = ref (fun x -> f := f2; f1 x) in
  (fun x -> !f x);;
您可以不使用毛茸茸的递归:

let doll f1 f2 =
  let f = ref f1 in
  f := (fun x -> f := f2; f1 x);
  (fun x -> !f x);;

(将递归替换为变异是一个常见的技巧;它实际上可以用来定义不动点,而不使用“rec”)

请注意,下面提出的OCaml解决方案都比JavaScript示例更“干净”,因为它们封装了“func”的可变性:在第一次调用之后,函数已永久更改,没有人可以访问引用将其更改回。这是通过使“f”引用位于“doll”调用的本地来实现的。当然,如果您希望引用仍然可以修改,也可以这样做。