OCaml中的副作用和顶级表达

OCaml中的副作用和顶级表达,ocaml,side-effects,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.print

我遇到了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;;
但我的输出总是:

_t1
_t1
_t1
_t1
我的输出应该是什么:

    _t0
    _t1
    _t2
    _t3
有什么办法解决我的问题吗,伙计


Thks all.

因为
tmp
是一个值,所以执行一次。将其更改为函数应使每次调用
tmp
时计数器增加

此外,为了简单起见,您可以返回
string
,而不是
string ref

let counter: int ref = ref (-1);;
let vargen: string = "_t";;

// tmp now is a function
let tmp() = incr counter; 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());;
你可以用

let tmp = 
  let counter = ref 0 in
  (fun () -> incr counter; vargen ^ (string_of_int !counter))

使用
tmp()
调用函数,如果希望计数器从0开始,则将0更改为-1。

当您编写
时,让tmp=ref foo
对表达式
foo
求值一次,以生成存储在引用中的值。访问引用将返回此值,而不重新计算原始表达式

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

克莱门特的解决方案是好的。思想

let counter =
  let count = ref (-1) in
  fun () -> incr count; !count
引用分配一次,但每次函数
fun()->incr count;,引用都会递增!调用count
。将引用放在函数的局部可以避免全局变量的一些陷阱。您可以将其视为函数
计数器的“静态变量”
,只是它是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 *)

如果需要在不增加计数器的情况下选择性地读取计数器,可以添加参数:

let counter =
  let count = ref (-1) in
    fun do_incr -> if do_incr then begin incr count end; !count;;
像这样使用它:

# counter true;;
- : int = 0
# counter true;;
- : int = 1
# counter true;;
- : int = 2
# counter false;;
- : int = 2
# counter false;;
- : int = 2
# counter true;;
- : int = 3

非常好的解释,现在我明白了为什么我的程序不工作了。非常好,这个解决方案非常适合我的问题,解决了它和其他一些问题。非常紧凑的pad解决方案,非常有帮助。Thks