通过使用异常,您一无所获。这是一个可能的解决方案。
(** 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
- 第二个参数是搜索函数,它接受 type 的状态
'a
并返回Solution (x,a)
where x
has type'b
和a
has type 'c
,或者Branches lst
where lst
has type 'a list
。