Functional programming 在OCaml中按对的键筛选对的列表

Functional programming 在OCaml中按对的键筛选对的列表,functional-programming,ocaml,Functional Programming,Ocaml,我有一个模拟字典的对的列表,比如[(“key1”,32);(“key2”,54);…],我需要一个函数,可以过滤列表并返回一个新列表,删除具有重复键的对 例如:像[(“key”,32);(“key”,78);(“anotherKey”,12)]这样的列表必须过滤为[(“key”,32);(“anotherKey”,12)] 我使用哈希表实现了这一点,但我更喜欢使用列表的解决方案。在下面的代码中,我找不到用列表替换哈希表的方法,因为在else分支中,我不知道如何正确更新seen列表 let get

我有一个模拟字典的对的列表,比如
[(“key1”,32);(“key2”,54);…]
,我需要一个函数,可以过滤列表并返回一个新列表,删除具有重复键的对

例如:像
[(“key”,32);(“key”,78);(“anotherKey”,12)]
这样的列表必须过滤为
[(“key”,32);(“anotherKey”,12)]

我使用哈希表实现了这一点,但我更喜欢使用列表的解决方案。在下面的代码中,我找不到用列表替换哈希表的方法,因为在
else
分支中,我不知道如何正确更新
seen
列表

let getKey (k,v) = k;;

(* Hashtable *)
let filter (list : (string * int list) : (string * int) list =
    let seen = Hashtbl.create (List.length list) in
        List.filter ( fun pair -> let exists = not (Hashtbl.mem seen (getKey pair)) in
            Hashtbl.replace seen (getKey pair) (); exists) list
;;

(* List *)
let filter (list : (string * int) list) : (string * int) list = 
    let seen = [] in
        List.filter ( fun pair -> let exists =  List.mem (getKey pair) seen in 
            if exists then failwith("duplicate")
            else (* what to do here? *) ; true) list
;;

我不太清楚问题出在哪里。对我来说,解决方案可以非常清楚地表达出来:

  • 将列表的头元素用于重复数据消除
  • 如果它不是结果列表的成员,则将head元素添加到结果列表中
  • 如果它已经是一个成员-跳过它
  • 对尾部进行重复数据消除(重复1-3次)
  • 实现方式如下所述:

    let rec is_member key = function
      | [] -> false
      | (k, _)::tail ->
        if k = key then true
        else is_member key tail
    ;;
    
    let dedup list =
      let rec aux acc = function
        | [] -> List.rev acc
        | ((k, v) as hd)::tl ->
          if is_member k acc then aux acc tl
          else aux (hd::acc) tl
      in aux [] list
    ;;
    
    utop # dedup [("key",32);("key",78);("anotherKey",12)];;
    - : (string * int) list = [("key", 32); ("anotherKey", 12)]
    
    关于标准库(尤其是列表),使用
    List.filter
    ,您无法实现目标,因为此函数不累加结果,而只是逐个迭代列表元素。如果需要累积结果,则应使用smth。像
    向左折叠
    ,例如:

    let dedup_list list =
      List.fold_left (fun acc ((k, v) as el) -> if is_member k acc then acc else el::acc) [] list
      |> List.rev
    ;;
    
    utop # dedup_list [("key",32);("key",78);("anotherKey",12)];;
    - : (string * int) list = [("key", 32); ("anotherKey", 12)]
    

    我不太清楚问题出在哪里。对我来说,解决方案可以非常清楚地表达出来:

  • 将列表的头元素用于重复数据消除
  • 如果它不是结果列表的成员,则将head元素添加到结果列表中
  • 如果它已经是一个成员-跳过它
  • 对尾部进行重复数据消除(重复1-3次)
  • 实现方式如下所述:

    let rec is_member key = function
      | [] -> false
      | (k, _)::tail ->
        if k = key then true
        else is_member key tail
    ;;
    
    let dedup list =
      let rec aux acc = function
        | [] -> List.rev acc
        | ((k, v) as hd)::tl ->
          if is_member k acc then aux acc tl
          else aux (hd::acc) tl
      in aux [] list
    ;;
    
    utop # dedup [("key",32);("key",78);("anotherKey",12)];;
    - : (string * int) list = [("key", 32); ("anotherKey", 12)]
    
    关于标准库(尤其是列表),使用
    List.filter
    ,您无法实现目标,因为此函数不累加结果,而只是逐个迭代列表元素。如果需要累积结果,则应使用smth。像
    向左折叠
    ,例如:

    let dedup_list list =
      List.fold_left (fun acc ((k, v) as el) -> if is_member k acc then acc else el::acc) [] list
      |> List.rev
    ;;
    
    utop # dedup_list [("key",32);("key",78);("anotherKey",12)];;
    - : (string * int) list = [("key", 32); ("anotherKey", 12)]
    

    您需要使筛选器递归并将更新的seen传递给下一个调用。您需要使筛选器递归并将更新的seen传递给下一个调用。