OCaml 断言失败

Posted

技术标签:

【中文标题】OCaml 断言失败【英文标题】:OCaml assertion fail 【发布时间】:2021-03-16 23:17:40 【问题描述】:

大家好,我是 OCaml 的新手,遇到了一些问题。所以我试图做一个递归函数,它接受一个非空列表并返回最后一个元素。我的列表可以是任何数据类型。 这是我现在所拥有的,它目前在断言语句中失败,说“此表达式的类型为 int,但表达式应为 int 列表类型。

let rec last l = match l with

[] -> []

|[x] -> [x]

|_::t -> last t ;;

assert(last [1; 2; 3; 4] = 4);;

assert(last ["a"; "b"; "c"; "d"] = "d");;

【问题讨论】:

【参考方案1】:

问题在于您的函数确实返回了一个列表,从这两行中可以看出:

[] -> []

|[x] -> [x]

您希望第二行是|[x] -> x。当然,你还需要处理空列表的情况并有效地返回一些东西。您可以让它引发异常、使用选项类型或让调用者指定默认值。

【讨论】:

是的,我刚刚尝试过并得到“此表达式的类型为 int 但表达式应为列表类型” 这(几乎可以肯定)是@PatJ 所说的“您需要处理空列表的情况”。如果将[x] 更改为x,则需要将[] 更改为某个不是列表的值。 没错,你处理空列表情况的方式会改变你想要的解决方案。【参考方案2】:

我经常发现处理这种类型错误的好方法是用我希望它具有的类型显式注释函数 - 在这种情况下,我希望你希望第一行类似于这个:

let rec last: 'a list -> 'a = fun l -> match l with

这样,错误变为:

File "1/hest.ml", line 5, characters 9-10:
5 | |[x] -> [x]
             ^
Error: This expression has type 'a list
       but an expression was expected of type 'a
       The type variable 'a occurs inside 'a list

这是一个更有帮助的错误。问题是,正如@PatJ 在他的回答中所写,当您编写函数时,您实际上返回了一个包含一个元素(或没有元素,当列表为空时)的列表,但是您对该函数的使用似乎表明您只想要最后一个元素。

您可以:

    更改函数的用法,因此断言与[4]["d"] 进行比较,而不仅仅是值。 将函数更改为返回Some xNone 而不是[x][],并将断言更改为针对Some 1Some "d" 进行断言。 更改函数以返回 x 而不是 [x] 并在空列表情况下抛出异常 - 那将是:failwith "empty list"

我认为第二种解决方案是最自然的。

如果您选择异常,则将函数重命名为 last_exn 会向用户传达他们必须注意函数可能会抛出的信息。

【讨论】:

【参考方案3】:

在这种情况下,最好使用选项类型:

let rec last_opt l =
  match l with
  | [] -> None
  | [x] -> Some x
  | _x::t -> last_opt t

let () =
 assert (last_opt [1; 2; 3; 4] = Some 4);
 assert (last_opt ["a"; "b"; "c"; "d"] = Some "d"); (* Note that here you could use chars, i.e. 'a', 'b' etc. instead of strings "a", "b" etc. *)
 assert (last_opt [] = None)

如果你真的不想要一个选项类型,你应该小心处理空的情况:

let rec last l =
  match l with
  | [] -> raise (Failure "last")
  | [x] -> x
  | _x::t -> last t

【讨论】:

以上是关于OCaml 断言失败的主要内容,如果未能解决你的问题,请参考以下文章

为啥流利的断言失败但断言通过了枚举?

有朋友遇到“断言失败”错误提示吗,怎么解决呢?

findcontours 断言失败

UIWindowController 断言失败

C# FluentAssertions 在断言失败后继续

选择器视图上的断言失败