2012-05-05 11 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 

解決するための任意のアイデア私の問題ガツ?

すべてが有効です。

答えて

8

let tmp = ref fooと書くと、式fooは一度評価され、参照に格納された値を生成します。参照にアクセスすると、元の式を再評価することなくこの値が返されます。

再評価を呼び起こす方法は、関数(fun() -> foo)を書くと、これは値です。それはそのまま返され、関数に渡され、参照に格納されます。この値に引数を適用するたびに、式fooが評価されます。

クレアメントのソリューションは良いです。

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

のアイデアは、参照が一度割り当てられますが、機能fun() -> incr count; !countが呼ばれるたびにインクリメントされていることです。関数にローカルな参照を持たせることで、グローバル変数の落とし穴が回避されます。 counterの「静的変数」と考えることができますが、これはOCamlスコープと評価ルールの自然な結果であり、追加の関数固有の概念ではありません。

あなたも、新鮮な、独立したカウンターにそれが呼ばれるたびに作成しても、より一般的なvargen発電機、書くことができます:あなたは、必要に応じてそれをインクリメントせずに読み込むことがカウンタが必要な場合は、ANを追加することができます

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

非常に良い説明、今私はなぜ私のプログラムisnt作業理解した。 – tsukanomon

3

tmpは値であるため、1回実行されます。それを関数に変更すると、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()を使用して関数を呼び出し、そしてあなたがカウンターをしたい場合は-1に0を変更を使用することができます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