【问题标题】:How to return the index of a for loop in OCaml?如何在 OCaml 中返回 for 循环的索引?
【发布时间】:2012-03-16 13:29:49
【问题描述】:
let find_free_next heap start = 
  for i = start to ((Array.length heap)-1) do
     match heap.(i) with 
     Hdr (Free (h), g)  ->
        i
  done

一旦找到匹配项,如何将循环的索引作为整数返回?

【问题讨论】:

    标签: functional-programming ocaml


    【解决方案1】:

    如果要坚持命令式风格,可以使用异常退出循环:


    exception Found of int
    
    let find_free_next heap start = 
      try
        for i = start to Array.length heap - 1 do
           match heap.(i) with 
           | Hdr (Free (h), g)  -> raise (Found i)
           | _ -> () (* If it is not what you are seeking *)
        done;
        raise Not_found   
      with
      | Found n -> n
    

    但一般来说,正如人们已经写过的那样,OCaml 中更偏爱函数式风格:


    let find_free_next heap start =
      let len = Array.length heap in
      let rec find i =
        if i >= len then None
        else 
          match heap.(i) with
          | Hdr (Free h, g) -> Some i
          | _ -> find (i+1)
      in
      find start
    

    在这个例子中,两个版本没有太大区别,但是使用异常退出循环/递归必须谨慎使用;你可以很容易地用它们引入控制流错误,而且它们有时很难调试。

    顺便说一句,您可以使用 Array.unsafe_get heap i 来加速您的数组访问,因为您可以确定 i 始终在上述示例数组的有效范围内。 (哦,不过,我们还需要 start >= 0 检查。)

    【讨论】:

      【解决方案2】:

      Asumu Takikawa 是对的,OCaml 中的for 循环不会返回结果。在惯用的 OCaml 中,您应该改用递归。理想情况下,会有一个像 List.find 这样适用于数组的标准函数。 OCaml Batteries Included 中有一个函数 BatArray.findi 可以满足您的需求。

      【讨论】:

        【解决方案3】:

        更简单、更高效(根本不需要分配):

        let rec find_free_next heap start =
          if start = Array.length heap then raise Not_found;
          match heap.(i) with
          | Hdr (Free h, g) -> i
          | _ -> find_free_start heap (i+1)
        

        或者,在命令式风格中:

        let exit = Exit
        let find_free_next heap start =
          let pos = ref (-1) in
          try
            for i = start to Array.length heap - 1 do
              match heap.(i) with
              | Hdr (Free h, g) -> pos := i; raise exit
              | _ -> ()
            done;
            raise Not_found
          with Exit -> !pos
        

        (请注意,raise exit 不分配只是因为预先计算的异常)。

        【讨论】:

        • “异常是预先计算的”,你的意思是在循环之前添加let exit = Exit 是什么防止了一些低效率?最近的 OCaml 编译器还会出现这种情况吗?
        【解决方案4】:

        Ocaml 中的循环应该是必要的,所以它不应该返回结果(除了单元)。所以如果你试图返回一个非单元的结果,编译器会给出一个警告。

        Ocaml 不允许您从循环返回结果的原因是因为这不是一个非常实用的习惯用法。如果您使用递归函数而不是循环,则很容易提前退出并返回结果(通过返回结果而不是递归)。如果您想编写惯用的 Ocaml,您可能希望在这种情况下使用递归。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-07-17
          • 1970-01-01
          • 2021-07-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多