2011-12-28 80 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 ...