2011-12-28 15 views
3

私は再帰関数factを持っています。この関数は、その内部の式かそれ以外の式から呼び出すことができます。OCamlで再帰関数の静的変数を定義する

Iは、可変vfactを関連付けたいたびfactvが初期化され、外部(別の関数)から呼び出され、その値はfact内で変更することができるが、ときに初期化することができない決してようfactの中から呼び出されます。

次のコードは、私のニーズに合った、が、1つの問題は、vは、グローバル変数として定義されていることである、と私は美しい見つからない外部からfactを呼び出す前にv := initを行う必要があります。

let init = 100 
let v = ref init 

let rec fact (n: int) : int = 
    v := !v + 1; 
    if n <= 0 then 1 else n * fact (n - 1) 

let rec fib (n: int) : int = 
    if n <= 0 then 0 
    else if n = 1 then (v := !v + 50; 1) 
    else fib (n-1) + fib (n-2) 

let main = 
    v := init; 
    print_int (fact 3); 
    print_int !v; (* 104 is expected *) 

    v := init; 
    print_int (fib 3); 
    print_int !v;; (* 200 is expected *) 

もっと良い実装をお考えですか?

+0

変数 'v'は' fact'関数よりも*スコープ*が大きいものの、* static *とは言いません。 – huitseeker

+0

@huitseeker:C言語の用語は、静的な*としてローカル関数の変数を定義すると、最初の呼び出しでのみ初期化され、後の呼び出しで同じ値が再利用されます。これは、関数呼び出し間で内部情報を伝播するために使用されることがよくあります。(初期のFortransのように、すべての関数変数が静的であるような言語もあります。つまり、コンパイラーはスタック上に動的なフレーム割り振りの概念を持たず、コンパイル時にすべてが割り当てられました。同じ関数が同時に生きています;再帰はありません) – gasche

答えて

3
そのデータは、様々な呼び出し間で共有されるようにするには、マーティンのソリューションを適応させることができ

let fact = 
    let counter = ref 0 in 
    fun n -> 
    let rec fact = ... in  
    fact n 

アイデアはlet fact = let counter = ... in fun n -> ...let fact = fun n -> let counter = ... in ...を変換することです:カウンターの代わりにfactの各呼び出しで、一度初期化されます。

このスタイルの古典的な例は次のとおりです。

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

は機能が多型であることを意味していた場合、あなたがトラブルを入力して入ることが用心:let foo = fun n -> ...は常に多型関数に一般化され、let foo = (let x = ref ... in fun n -> ...)はないが、それは不健全になるので、fooは多型を持たないでしょう。あなたもカウンター工場に上記カウンタの例を一般化することができます

make_counter()をコールするたびに

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

、新しいカウンタを取得し、それがコール間で状態を共有する関数であるが、その状態は以前のmake_counter()カウンタの作成とは独立しています。

5

次のようにあなたが含む関数の本体内の関数と値の定義を非表示にすることができます:OCamlでのオブジェクトで

open Printf 

let init = 100 

let fact n = 
    let rec fact counter n = 
    incr counter; 
    if n <= 0 then 1 else n * fact counter (n - 1) 
    in 
    let counter = ref init in 
    let result = fact counter n in 
    (result, !counter) 

let main() = 
    let x, count = fact 3 in 
    printf "%i\n" x; 
    printf "counter: %i\n" count (* 104 is expected *) 

let() = main() 
+0

あなたのコメントをありがとう...私はあなたのコードに関する一つの発言を持っています:内側の 'fact'が' fact'の2つの呼び出しを含んでいる場合、 'counter'私が欲しいものではない: '...そしてその値は実際には内部で変更できる...' – SoftTimur

+1

あるいは 'let fact = .' –

+0

私は自分のOPに 'fib'を追加して、私が何を望んでいるかを説明しました... – SoftTimur

1

を、あなたが行うことができます:

class type fact_counter = object ('self) 
    method get : int 
    method set : int -> unit 
    method inc : unit 
    method fact : int -> int 
end;; 

class myCounter init : fact_counter = object (self) 
    val mutable count = init 
    method get = count 
    method set n = count <- n 
    method inc = count <- count + 1 
    method fact n = 
    self#inc; 
    if n <= 0 then 1 else n * self#fact (n - 1) 
end;; 

その後、あなたが取得することができます。

# let c = new myCounter 0;; 
val c : myCounter = <obj> 
# c#fact 10;;    
- : int = 3628800 
# c#get;;     
- : int = 11 
# c#set 42;;    
- : unit =() 
# c#fact 10;;    
- : int = 3628800 
# c#get;;  
- : int = 53 

に0123を含める方法を簡単に確認できたら...

関連する問題