2011-12-28 79 views
3

我有一個遞歸函數fact,它可以從它內部的表達式或其外部的表達式中調用。在OCaml中爲遞歸函數定義一個靜態變量

我想fact具有可變v相關聯,使得每次fact(另一功能)調用,v被初始化,並且其值可以內部fact被改變,但絕不可時被初始化fact內部調用

下面的代碼適合我的需要,但一個問題是,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'函數更大的* scope *,我不會將其稱爲* static *。 – huitseeker 2011-12-28 20:24:04

+0

@huitseeker:我想這個術語來自C語言,如果你將一個局部函數變量定義爲* static *,它只會在第一次調用時被初始化,並且在以後的調用中會重複使用相同的值。這通常用於跨函數調用傳播內部信息。(甚至有早期的Fortrans等語言,其中所有的函數變量都是靜態的,也就是說編譯器沒有在棧上動態分配幀的概念,所有的東西都是在編譯時分配的,特別是你不能有兩次調用相同的函數同時存在;沒有遞歸) – gasche 2011-12-28 23:26:19

回答

3

,使數據在各種調用共享您可以適應馬丁的解決方案:

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

的想法是把let fact = fun n -> let counter = ... in ...let fact = let counter = ... in fun n -> ...:計數器被初始化一次,而不是在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將不會有多態類型。

你甚至可以概括以上對計數器工廠的反例:

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

每次調用make_counter(),你會得到一個新的計數器,那就是整個呼叫共享狀態的功能,但其國家獨立於以前的make_counter()櫃檯創作。

5

您可以隱藏包含函數體內的功能及價值定義如下:

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'調用,他們'counter'的值彼此獨立,這不完全是我想要的:'...和它的價值可以在事實內部改變......' – SoftTimur 2011-12-28 19:43:56

+1

或'讓事實n =讓計數器= ref init讓我們記住事實n = .. 。' – 2011-12-28 19:44:46

+0

我在我的OP中添加了'fib'來說明我想要的內容...... – SoftTimur 2011-12-28 19:57:27

1

隨着Ocaml程序編寫的對象,你可以這樣做:

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 

我希望你可以很容易地看到如何適應myCounter包括fib ...