List.assoc 使用 List.find

问题描述

我想使用 List.assoc 实现 List.find 函数,这是我尝试过的:

let rec assoc lista x = match lista with
  | [] -> raise Not_found
  | (a,b)::l -> try (List.find (fun x -> a = x) lista)
            b
        with Not_found -> assoc l x;;

但它给了我这个错误

该表达式的类型为 ('a * 'b) 列表,但预期表达式的类型为 'a 列表 类型变量 'a 出现在 'a * 'b

我不知道这是预期发生的事情还是我做错了什么。我也试过这个作为替代:

let assoc lista x = match lista with
    | [] -> raise Not_found
    | (a,b)::l -> match List.split lista with 
        | (l1,l2) -> let ind = find l1 (List.find (fun s -> compare a x = 0))
        in List.nth l2 ind;;

其中 find一个返回所请求元素索引的函数

let rec find lst x =
    match lst with
    | [] -> raise Not_found
    | h :: t -> if x = h then 0 else 1 + find t x;;

这段代码的问题是函数的类型应该是('a * 'b) list -> 'a -> 'b,而是(('a list -> 'a) * 'b) list -> ('a list -> 'a) -> 'b,所以当我尝试

assoc [(1,a);(2,b);(3,c)] 2;; 

我明白了:

此表达式的类型为 int 但应为类型的表达式 'a list -> 'a(指列表中的一对的第一个元素)

我不明白为什么我没有得到预期的函数类型。

解决方法

首先,关于使您的 assoc 函数更符合 OCaml 习惯的快速建议:让它将列表作为最后一个参数。

其次,您为什么要尝试在查找方面实现这一点?没有它就容易多了。

let rec assoc x lista =
  match lista with
  | [] -> raise Not_found
  | (a,b) :: xs -> if a = x then b else assoc x xs

使用 OCaml 中的列表工作方式,这样的事情更简单、更高效。

将列表作为最后一个参数,甚至意味着我们可以写得更简洁。

let rec assoc x =
  function
  | [] -> raise Not_found
  | (a,b) :: xs -> if a = x then b else assoc x xs

对于您的问题,OCaml 从函数的使用方式推断函数的类型。

find l1 (List.find (fun s -> compare a x = 0))

我们知道 l1 是一个 int 列表。因此,我们必须尝试在 int 列表列表中找到它。所以:

List.find (fun s -> compare a x = 0)

必须返回一个 int list 列表。一团糟。试着重新思考你的函数,你最终会得到更容易推理的东西。