如何在Ocaml中实现俄罗斯娃娃图案?

时间:2011-03-20 19:09:27

标签: ocaml

在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',它就不会调用后续函数......它总是打印'First ...'。

2 个答案:

答案 0 :(得分:10)

yzzlr答案非常好,但有两个评论:

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

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”)

答案 1 :(得分:9)

这很简单,但你需要使用副作用。这是一个以两个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