質問

I am in trouble with ocaml.

I want to make a function that will increment my counter every time I call it and concat my vargen string with the counter number and return this new string.

What I did without success is:

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;;

But my output are always:

_t1
_t1
_t1
_t1

And what my output should be:

    _t0
    _t1
    _t2
    _t3

Any ideas to solve my problem guyz?

Thks all.

役に立ちましたか?

解決

When you write let tmp = ref foo, the expression foo is evaluated once, to produce a value which is stored in the reference. Accessing the reference returns this value, without re-evaluating the original expression.

The way to provoke re-evaluation is to use a function instead: if you write a function (fun () -> foo), this is a value: it is returned as is, passed to functions, stored in references. And each time you apply an argument to this value, the expression foo is evaluated.

Clément's solution is good. The idea of

let counter =
  let count = ref (-1) in
  fun () -> incr count; !count

Is that the reference is allocated once, but incremented each time the function fun () -> incr count; !count is called. Having the reference local to the function avoids some of the pitfall of global variables. You can think of it as a "static variable" of the function counter, only it is a natural result of OCaml scoping and evaluation rules and not an additional, function-specific concept.

You can even write an even more general vargen generator, that creates fresh, independent counters each time it's called:

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

他のヒント

You could use

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

Call the function using tmp (), and change 0 to -1 if you want the counter to start at 0.

Since tmp is a value, it is executed once. Changing it into a function should make the counter increase each time tmp is invoked.

Furthermore, you could return string instead of string ref for simplicity:

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());;

If you need the counter to be optionally read without incrementing it, you can add an argument:

let counter =
  let count = ref (-1) in
    fun do_incr -> if do_incr then begin incr count end; !count;;

Using it like:

# counter true;;
- : int = 0
# counter true;;
- : int = 1
# counter true;;
- : int = 2
# counter false;;
- : int = 2
# counter false;;
- : int = 2
# counter true;;
- : int = 3
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top