OCaml中的副作用和顶级表达式

tsu*_*mon 4 ocaml side-effects

我和ocaml有麻烦.

我想创建一个函数,每次调用它时都会递增计数器,并用计数器号连接我的vargen字符串并返回这个新字符串.

我没有成功的是:

let (counter : int ref) = ref 0;;
let (vargen : string) = "_t";;
let tmp = incr counter;ref (vargen ^ string_of_int !counter);;
Printf.printf "%s\n" !tmp;;
Printf.printf "%s\n" !tmp;;
Printf.printf "%s\n" !tmp;;
Printf.printf "%s\n" !tmp;;
Run Code Online (Sandbox Code Playgroud)

但我的输出总是:

_t1
_t1
_t1
_t1
Run Code Online (Sandbox Code Playgroud)

我的输出应该是什么:

    _t0
    _t1
    _t2
    _t3
Run Code Online (Sandbox Code Playgroud)

有什么想法来解决我的问题吗?

总而言之.

gas*_*che 8

在编写时let tmp = ref foo,表达式将foo被计算一次,以生成存储在引用中的值.访问引用将返回此值,而无需重新评估原始表达式.

激发重新评估的方法是使用函数:如果你编写一个函数(fun () -> foo),这是一个值:它按原样返回,传递给函数,存储在引用中.每次将参数应用于此值时,foo都会计算表达式.

Clément的解决方案很好.的想法

let counter =
  let count = ref (-1) in
  fun () -> incr count; !count
Run Code Online (Sandbox Code Playgroud)

是引用是分配一次,但每次fun () -> incr count; !count调用该函数时都会递增.使函数的局部参考避免了全局变量的一些缺陷.您可以将其视为函数的"静态变量" counter,只是它是OCaml范围和评估规则的自然结果,而不是额外的,功能特定的概念.

您甚至可以编写一个更通用的vargen生成器,每次调用时都会创建新的独立计数器:

let make_vargen prefix =
   let count = ref (-1) in
   fun () ->
     incr count;
     prefix ^ string_of_int !count

let fresh_t = make_vargen "t"
let () = print_endline (fresh_t ())  (* t0 *)
let () = print_endline (fresh_t ())  (* t1 *)
let fresh_u = make_vargen "u"
let () = print_endline (fresh_u ())  (* u0 *)
let () = print_endline (fresh_t ())  (* t2 *)
let () = print_endline (fresh_u ())  (* u1 *)
Run Code Online (Sandbox Code Playgroud)