减去列表OCAML中的元素

问题描述

let rec subtract_list myl = match myl with
    | [] -> 0
    | a::rest-> List.fold_right (fun y z -> y-z ) myl 0

我想从列表中减去列表中的元素,但结果不正确

subtract_lst [11; 1; 1; 1] = 8,但是我得到的结果是10。这是怎么了?

解决方法

问题在于fold_right以这种方式工作(https://caml.inria.fr/pub/docs/manual-ocaml/libref/List.html):

List.fold_right f [a1; ...; an] b is f a1 (f a2 (... (f an b) ...)). Not tail-recursive.

所以你有什么:

11 - (1 - (1 - 1))

您可以通过更改代码来解决它:

let subtract_list myl = 
    match myl with
    | [] -> 0
    | a::rest -> List.fold_left (fun acc e -> acc - e) a rest
;;

或:

let subtract_list myl = if myl = [] then 0 else
    List.fold_left (fun acc e -> acc - e) (List.hd myl) (List.tl myl)
;;
,

您遇到的问题是,尽管您的列表包含同质值,但含义并不相同。

对于您来说,使用明确的类型(例如:

type t =
  ( int         (* The substracted *)
  * int list )  (* The subtractors *)

允许您编写此功能:

let subtract_list
  : t -> int
  = function
    | (i,[]) -> i
    | (i,tl) -> List.fold_left (-) i tl

有了列表并遵循相同的模式,您可以编写此函数:

let subtract_list
  : int list -> int
  = function
    | [] -> 0 (* Empty list,nothing to do *)
    | i::[] -> i (* Case where you have nothing to substract *)
    | i::tl -> List.fold_left (-) i tl

注意:我用短路来减去元素List.fold_left (-) i tlList.fold_left (fun x y -> x - y) i tl相同