为什么此OCaml代码段的类型错误?

时间:2014-10-21 04:05:55

标签: ocaml memoization

我正在学习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>

看起来很奇怪,我不知道哪个部分的实现出错了。

3 个答案:

答案 0 :(得分:1)

考虑这个表达式的类型:

  match Hashtbl.find cache v with
  | Value r -> r
  | Exn e -> e

在某些情况下,它会返回r,在其他情况下会返回e。因此,这些必须是相同的类型。由于您将函数的值缓存为r而异常缓存为e,因此r是一个例外。

如果您的表中有什么,那么您更有可能提出异常。

答案 1 :(得分:1)

正如Jeffrey建议的那样,当Exn eNot_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,则代码的这一部分意味着

  1. r的类型= e
  2. 的类型
  3. r的类型=在with
  4. 之后将返回的内容

    然后让我们假设它捕获了指示未找到的异常:

    | 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 -> ...已经存在,所以eexn,并且回想起r的类型= e的类型,所以r&#39}的类型是exn

    所以整个memo_func_hash_exp会返回exn的类型,对吗?

    由于也可能会返回r = f vr,因此第二个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可能会抛出)。