我正在学习OCaml中“memoized function”的概念,我试图自己实现。
这是我写的代码:
type 'a memo_type = Value of 'a | Exn of exn
let memo_func_hash_exp f =
let cache = Hashtbl.create 10 in
fun v ->
try
match Hashtbl.find cache v with
| Value r -> r
| Exn e -> e
with
| Not_found ->
begin
try
let r = f v in
Hashtbl.replace cache v (Value r);
r
with
| e ->
Hashtbl.replace cache v (Exn e);
raise e
end ;;
然而,令我惊讶的是,口译员告诉我memo_func_hash_exp
函数的类型是这样的:
val memo_func_hash_exp : ('a -> exn) -> 'a -> exn = <fun>
看起来很奇怪,我不知道哪个部分的实现出错了。
答案 0 :(得分:1)
考虑这个表达式的类型:
match Hashtbl.find cache v with
| Value r -> r
| Exn e -> e
在某些情况下,它会返回r
,在其他情况下会返回e
。因此,这些必须是相同的类型。由于您将函数的值缓存为r
而异常缓存为e
,因此r
是一个例外。
如果您的表中有什么,那么您更有可能提出异常。
答案 1 :(得分:1)
正如Jeffrey建议的那样,当Exn e
为Not_found
时会遇到麻烦,因为该异常与Hashtbl.find
引发的异常相混淆,表示缺席。您可以通过重新排列代码以将raise e
移出问题try
的范围来避免这种情况:
type 'a memo = Value of 'a | Exn of exn
let memo_func f =
let cache = Hashtbl.create 16 in
fun x ->
let result =
try Hashtbl.find cache x
with Not_found ->
let entry =
try Value (f x)
with e -> Exn e in
Hashtbl.add cache x entry;
entry in
match result with
| Value v -> v
| Exn e -> raise e
答案 2 :(得分:1)
以下是您的代码:
type 'a memo_type = Value of 'a | Exn of exn
let memo_func_hash_exp f =
let cache = Hashtbl.create 10 in
fun v ->
try
match Hashtbl.find cache v with
| Value r -> r
| Exn e -> e
with
| Not_found ->
begin
try
let r = f v in
Hashtbl.replace cache v (Value r);
r
with
| e ->
Hashtbl.replace cache v (Exn e);
raise e
end ;;
让我们破解你的代码。
首先导入部分是
try
match Hashtbl.find cache v with
| Value r -> r
| Exn e -> e
with
基本上,你要做的是1)。如果Hashtbl找到键v
,则返回其值; 2)。如果没有找到,则抛出异常,外部try with
将捕获它。
但是,您的代码出现了第一个错误:您应该raise e
,而不是在| Exn e ->
之后返回。
无论如何,如果您使用e
代替raise e
,则代码的这一部分意味着
r
的类型= e
r
的类型=在with
然后让我们假设它捕获了指示未找到的异常:
| Not_found ->
begin
try
let r = f v in
Hashtbl.replace cache v (Value r);
r
with
| e ->
Hashtbl.replace cache v (Exn e);
raise e
end
你知道,with | e -> ...
已经存在,所以e
是exn
,并且回想起r的类型= e的类型,所以r&#39}的类型是exn
。
所以整个memo_func_hash_exp
会返回exn
的类型,对吗?
由于也可能会返回r = f v
和r
,因此第二个r
的类型也为exn
。
总的来说,val memo_func_hash_exp : ('a -> exn) -> 'a -> exn = <fun>
正确的版本可能是:
type 'a memo_type = Value of 'a | Exn of exn
let memo_func_hash_exp f =
let cache = Hashtbl.create 10 in
fun v ->
if Hashtbl.mem cache v then
match Hashtbl.find cache v with
| Value r -> r
| Exn e -> raise e
else
try let r = f v in Hashtbl.add cache v (Value r); r
with e -> Hashtbl.add cache v (Exn e); raise e
基本上,你使用直接Hashtbl.mem
来检查成员资格,这样可以避免混淆异常(一个exn可能因为找不到而被抛出,另一个可能是因为f可能会抛出)。