Side effects and top-level expressions in OCaml

I have problems with ocaml.

I want to create a function that will increment the counter every time I call it, and concatenate the vargen line with the counter number and return this new line.

What I did without success:

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 conclusion is always:

 _t1 _t1 _t1 _t1 

And what should be my conclusion:

  _t0 _t1 _t2 _t3 

Any ideas to solve my guyz problem?

Thks all.

+4
source share
4 answers

When you write let tmp = ref foo , the expression foo is evaluated once to get the value that is stored in the link. Accessing the link returns this value without re-evaluating the original expression.

A way to re-evaluate is to use a function: if you write a function (fun () -> foo) , this value: it is returned as is, passed to the function, stored in links. And every time you apply an argument to this value, the expression foo evaluated.

ClΓ©ment's solution is good. Idea

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

Is this link assigned once, but incremented every time the fun () -> incr count; !count function is called fun () -> incr count; !count fun () -> incr count; !count . The presence of a function reference eliminates some error in global variables. You can consider it as a "static variable" of the counter function, only this is a natural result of the OCAML rules for defining the scope and evaluation, and not for an additional functional concept.

You can even write an even more general vargen generator, which creates fresh independent counters each time:

 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 *) 
+8
source

Since tmp is a value, it runs once. Changing it to a function should increment the counter every time tmp is called.

Alternatively, you can 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());; 
+3
source

you can use

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

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

+3
source

If you want the counter to be read additionally without increasing 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 
+1
source

Source: https://habr.com/ru/post/1410815/


All Articles