2012-05-05 55 views
4

我遇到ocaml問題。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 

解決任何想法我問題guyz?

這一切。

回答

8

當您編寫let tmp = ref foo時,表達式foo被評估一次,以產生存儲在參考中的值。訪問參考返回此值,而不重新評估原始表達式。

引發重新評估的方法是使用函數:如果您編寫函數(fun() -> foo),則這是一個值:它按原樣返回,傳遞給存儲在引用中的函數。每次您將參數應用於此值時,都會評估表達式foo

Clément的解決方案很好。的

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

的想法是,參照被分配一次,但每個功能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 *) 
+0

非常好的解釋,現在我明白了爲什麼我的程序不工作。 – tsukanomon

3

由於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());; 
+0

Ve的好的,這個解決方案完全適合我的問題,解決它和我有的其他問題。 – tsukanomon

3

你可以使用

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

呼叫使用tmp()的功能,如果你想在櫃檯更改0到-1從0開始。

+0

非常緊湊的墊的解決方案,它非常有幫助。 Thks – tsukanomon

1

如果需要櫃檯不增加它可以選擇性閱讀,你可以添加一個參數:

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