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

为什么此OCaml代码段的类型错误?,ocaml,memoization,Ocaml,Memoization,我正在学习OCaml中“记忆化函数”的概念,并尝试自己实现 以下是我编写的代码: 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 -&g

我正在学习OCaml中“记忆化函数”的概念,并尝试自己实现

以下是我编写的代码:

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\u func\u hash\u exp
的类型如下:

val memo_func_hash_exp : ('a -> exn) -> 'a -> exn = <fun>
val memo\u func\u hash\u exp:('a->exn)->'a->exn=

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

考虑此表达式的类型:

  match Hashtbl.find cache v with
  | Value r -> r
  | Exn e -> e
在某些情况下,它返回
r
,在其他情况下,它返回
e
。因此,它们必须是相同的类型。由于将函数值缓存为
r
,将异常缓存为
e
,因此
r
是异常


如果表中存在异常,则更可能希望引发异常。

考虑此表达式的类型:

  match Hashtbl.find cache v with
  | Value r -> r
  | Exn e -> e
在某些情况下,它返回
r
,在其他情况下,它返回
e
。因此,它们必须是相同的类型。由于将函数值缓存为
r
,将异常缓存为
e
,因此
r
是异常


如果表中存在异常,则更可能希望引发异常。

考虑此表达式的类型:

  match Hashtbl.find cache v with
  | Value r -> r
  | Exn e -> e
在某些情况下,它返回
r
,在其他情况下,它返回
e
。因此,它们必须是相同的类型。由于将函数值缓存为
r
,将异常缓存为
e
,因此
r
是异常


如果表中存在异常,则更可能希望引发异常。

考虑此表达式的类型:

  match Hashtbl.find cache v with
  | Value r -> r
  | Exn e -> e
在某些情况下,它返回
r
,在其他情况下,它返回
e
。因此,它们必须是相同的类型。由于将函数值缓存为
r
,将异常缓存为
e
,因此
r
是异常


如果表中存在异常,则更可能希望引发异常。

正如Jeffrey所建议的,当
Exn e
未找到时,这会遇到问题,因为该异常与
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

正如Jeffrey所建议的,当
Exn e
未找到
时,这会遇到麻烦,因为该异常与
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

正如Jeffrey所建议的,当
Exn e
未找到
时,这会遇到麻烦,因为该异常与
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

正如Jeffrey所建议的,当
Exn e
未找到
时,这会遇到麻烦,因为该异常与
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
以下是您的代码:

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
将捕获它

但是,代码出现的第一个错误是:您应该
提高e
,而不是在
之后返回e

无论如何,如果您使用
e
而不是
raisee
,这部分代码意味着

  • r的类型
    =
    e的类型
  • r的类型
    =使用
  • 然后,假设它捕获了表示未找到的异常:

    | 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
    
    你看,
    和| e->…
    在那里,所以
    e
    exn
    ,回忆r的类型=e的类型,所以r的类型是
    exn

    所以整个
    memo\u func\u hash\u exp
    将返回
    exn
    的类型,对吗

    由于还可能返回
    r=f v
    r
    ,因此第二个
    r
    也具有
    exn
    类型

    所以总体而言,
    valmemo\u func\u hash\u exp:('a->exn)->'a->exn=


    正确的版本可能是:

    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
    
    基本上,您首先使用direct
    Hashtbl.mem
    检查成员资格,这将避免异常混淆(一个exn可能由于找不到而抛出,另一个可能由于f可能抛出的exn而抛出)。

    下面是您的代码:

    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
    将捕获它

    但是,代码出现的第一个错误是:您应该
    提高e
    ,而不是在
    之后返回e

    无论如何,如果您使用
    e
    而不是
    raisee
    ,这部分代码意味着

  • r的类型
    =
    e的类型
  • r的类型
    =使用
  • 然后,假设它捕获了表示未找到的异常:

    | 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
    
    你看,
    和| e->…
    在那里,所以
    e
    exn
    ,回忆r的类型=e的类型,所以r的类型是
    exn

    所以整个
    memo\u func\u hash\u exp
    将返回
    exn
    的类型,对吗

    因为
    r=f v