【问题标题】:How to pass the return type of a function to an exception in OCaml?如何将函数的返回类型传递给 OCaml 中的异常?
【发布时间】:2013-01-11 09:30:52
【问题描述】:

我在 OCaml 中有函数“my_a”,它可能有一个非常复杂的返回类型:

exception Backtrack
exception Continue of (* How do I put the type of function 'my_a' here? *)

let my_a arg = try do_stuff (List.hd arg) 
               with 
               | Backtrack -> my_a (List.tl arg)
               | Continue (found_answer) ->  (try my_a (List.tl arg)
                                              with 
                                     | Backtrack -> raise Continue(found_answer)
                                     | Continue (other_answer) -> 
                       raise Continue (compare_answer(found_answer,other_answer));;
(* the caller of my_a will handle the Continue exception to catch the found value
if something was found*)

这是我的问题:我正在使用回溯来寻找解决方案。当 do_stuff 引发回溯异常时,没有解决方案走这条路。但是,当它引发 Continue 类型的异常时,这意味着它找到了解决方案,但是,它可能不是最好的解决方案,那就是当我用不同的路径再次尝试时。如果还有其他异常,我想返回它已经找到的答案。

问题是,为了能够使用 OCaml 的该功能,我需要告诉它 Continue 将携带什么数据类型。当我定义 my_a 时,OCaml 顶层返回什么:

   'a * ('a -> ('a, 'b) symbol list list) ->
  'b list -> ('a * ('a, 'b) symbol list) list * 'b list = <fun>

有没有人知道如何做到这一点,或者有不同的解决方案?

【问题讨论】:

  • 您应该告诉我们您要解决的问题。由于您认为函数名称可以以大写字母开头,同时您以非常规的方式使用异常,如果您允许我们告诉您如何解决您的原始问题,我们可以更好地帮助您,而不是向我们询问有关返回类型的具体技术问题。
  • 您好,我提供了更多信息,希望您能帮助我
  • 这听起来不对。如果Continue 已经发生,然后另一个Continue 发生,您的函数将返回第一个Continue 找到的结果,但是您在文中说您应该比较找到的两种解决方案,以便您可以使用更好的解决方案.在任何情况下,您都不应该围绕这样的异常来构建您的程序。
  • 在搜索整个空间之前,您无法知道最佳解决方案,因此这只是一个详尽的搜索。或者你打算在某个时候修剪搜索?
  • 好吧,我确实打算返回最佳解决方案,忽略任何其他选择。我想我应该显示更多关于我的想法的信息。问题是我有点无法完全实现它。

标签: exception ocaml return-type


【解决方案1】:

很难准确地说出你在问什么。我想您可能会问如何将 Two 异常中的类型设置为 A 的返回类型,而无需专门声明此类型。我想不出任何办法。

如果您使用选项类型而不是异常,事情可能会更好。或者您可以明确声明 A 的返回类型。这可能是很好的文档。

一些附带的 cmets:(a) 函数名称必须以小写字母开头 (b) 这段代码看起来相当复杂且难以理解。可能有一种更简单的方法来构建您的计算。

【讨论】:

  • “无法写下类型”看起来像是一种设计气味,我认为与其通过其他技巧来获得更多推理,不如直面这种设计气味。程序的类型结构与其术语结构一样重要,如果推理允许我们避免冗余,那么它不应该被用来失去对它的控制。定义类型同义词以捕获领域抽象,然后一个类型不应该是全局难吃的。
【解决方案2】:

使用异常你什么也得不到。这是一个可能的解决方案。

(** There are many ways to implement backtracking in Ocaml. We show here one
    possibility. We search for an optimal solution in a search space. The
    search space is given by an [initial] state and a function [search] which
    takes a state and returns either

    - a solution [x] together with a number [a] describing how good [x] is
      (larger [a] means better solution), or

    - a list of states that need still to be searched.

    An example of such a problem: given a number [n], express it as a sum
    [n1 + n2 + ... + nk = n] such that the product [n1 * n2 * ... * nk] is
    as large as possible. Additionally require that [n1 <= n2 <= ... <= nk].
    The state of the search can be expressed as pair [(lst, s, m)] where
    [lst] is the list of numbers in the sum, [s] is the sum of numbers in [lst],
    and [m] is the next number we will try to add to the list. If [s = n] then
    [lst] is a solution. Otherwise, if [s + m <= n] then we branch into two states:

    - either we add [m] to the list, so the next state is [(m :: lst, m+s, m)], or
    - we do not add [m] to the list, and the next state is [(lst, s, m+1)].

    The return type of [search] is described by the following datatype:
*)

type ('a, 'b, 'c) backtrack =
  | Solution of ('a * 'b)
  | Branches of 'c list

(** The main function accepts an initial state and the search function. *)
let backtrack initial search =
  (* Auxiliary function to compare two optional solutions, and return the better one. *)
  let cmp x y =
    match x, y with
      | None, None -> None (* no solution *)
      | None, Some _ -> y  (* any solution is better than none *)
      | Some _, None -> x  (* any solution is better than none *)
      | Some (_, a), Some (_, b) ->
        if a < b then y else x
  in
  (* Auxiliary function which actually performs the search, note that it is tail-recursive.
     The argument [best] is the best (optional) solution found so far, [branches] is the
     list of branch points that still needs to be processed. *)
  let rec backtrack best branches =
    match branches with
      | [] -> best (* no more branches, return the best solution found *)
      | b :: bs ->
        (match search b with
          | Solution x ->
            let best = cmp best (Some x) in
              backtrack best bs
          | Branches lst ->
            backtrack best (lst @ bs))
  in
    (* initiate the search with no solution in the initial state *)
    match backtrack None [initial] with
      | None -> None (* nothing was found *)
      | Some (x, _) -> Some x (* the best solution found *)

(** Here is the above example encoded. *)
let sum n =
  let search (lst, s, m) =
    if s = n then
      (* solution found, compute the product of [lst] *)
      let p = List.fold_left ( * ) 1 lst in
        Solution (lst, p)
    else
      if s + m <= n then
        (* split into two states, one that adds [m] to the list and another
           that increases [m] *)
        Branches [(m::lst, m+s, m); (lst, s, m+1)]
      else
        (* [m] is too big, no way to proceed, return empty list of branches *)           
        Branches []
  in
    backtrack ([], 0, 1) search
;;

(** How to write 10 as a sum of numbers so that their product is as large as possible? *)
sum 10 ;; (* returns Some [3; 3; 2; 2] *)

OCaml 高兴地告诉我们backtrack 的类型是

'a -> ('a -> ('b, 'c, 'a) backtrack) -> 'b option

这是有道理的:

  • 第一个参数是初始状态,它有一些类型'a
  • 第二个参数是搜索函数,它的状态类型为'a 和 返回Solution (x,a),其中x 的类型为'ba 的类型为'c, 或Branches lst,其中lst 的类型为'a list

【讨论】:

    猜你喜欢
    • 2018-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-29
    • 1970-01-01
    • 1970-01-01
    • 2021-04-02
    • 1970-01-01
    相关资源
    最近更新 更多