我正在学习OCaml。我知道OCaml为我们提供了强制性的编程风格和函数式编程。
我在计算OCaml中第n个Fibonacci数的课程中遇到了这段代码
let memoise f =
let table = ref []
in
let rec find tab n =
match tab with
| [] ->
let v = (f n)
in
table := (n, v) :: !table;
v
| (n', v) :: t ->
if n' = n then v else (find t n)
in
fun n -> find !table n
let fibonacci2 = memoise fibonacci1
函数fibonacci1
以标准方式实现如下:
let rec fibonacci1 n =
match n with
| 0 | 1 -> 1
| _ -> (fibonacci1 (n - 1)) + (fibonacci1 (n - 2))
现在我的问题是我们如何在fibonacci2中实现备忘录。 {(1}}已在函数fibonacci2中定义,因此,我的逻辑规定在函数完成计算后,列表table
应该丢失,并且在每次调用之后,表将一次又一次地构建。
我运行了一个简单的测试,我在OCaml REPL中调用函数table
两次,第二个函数调用返回的答案明显快于第一次调用函数(与我的期望相反)。
我认为,如果使用fibonacci 35
声明变量,默认情况下可以使用全局范围。
所以我试过这个
ref
但这给了我一个错误,说x的值是无限的。
为什么这样做?
答案 0 :(得分:3)
函数memoise
返回一个值,称之为f
。 (f
恰好是一个函数)。该值的一部分是表格。每次拨打memoise
时,您都会获得不同的值(使用不同的表格)。
在示例中,返回值f
的名称为fibonacci2
。所以,名为fibonacci2
的东西里面有一个表,可以被函数f
使用。
默认情况下没有全局范围,这将是一个巨大的混乱。无论如何,这是一个终身而非范围的问题。只要能以某种方式到达对象,OCaml的生命周期就会持续。对于表格,可以通过返回的函数到达它,因此只要函数有效,它就会持续。
在第二个示例中,您正在测试x
的范围(而不是生命周期),实际上x
的范围仅限于其let
的子表达式。 (即,它仅在表达式y
中有意义,它不使用它。)在原始代码中,table
的所有用法都在其let
内,因此没有问题。
尽管引用有点棘手,但OCaml的基本语义来自lambda演算,并且非常干净。这就是为什么在OCaml(恕我直言)中编码是如此令人高兴。