如何使用 OCaml 将两个列表中的每个单独元素压缩到一个列表中

Posted

技术标签:

【中文标题】如何使用 OCaml 将两个列表中的每个单独元素压缩到一个列表中【英文标题】:How to zip each individual element from two lists into one list using OCaml 【发布时间】:2020-05-10 02:20:50 【问题描述】:

如果我有一个元组的输入,其中包含两个相同长度的整数列表,并且我希望我的输出是压缩后的这两个列表的列表,那么在从元组中提取这两个列表之后,如何压缩每个人元素到一个列表中?例如,如果我的输入是 twolists= ([1;2;3], [4;5;6]),那么我希望我的输出是 [(1,4); (2,5); (3,6)]。如何压缩每个元素并将其添加到我的输出中? 函数名称和类型如下:

let rec pairlists twolists = ...

val pairlists : 'a list * 'b list -> ('a * 'b) list = fun

到目前为止我有:

let rec pairlists twolists = 
  let (l1, l2) = twolists in
  let rec zip (l1,l2) =
    match l1 with 
    |[] -> l2
    |x :: xs -> x :: zip(l2, xs) in
  twolists ;;

但这显然不是我想要的。

【问题讨论】:

要将文本片段标记为代码,只需用鼠标选择它并单击按钮,在编辑器窗口中看起来像 【参考方案1】:

您在寻找List.combine 吗?

val combine : 'a 列表 -> 'b 列表 -> ('a * 'b) 列表

将一对列表转换成对列表:combine [a1; ...;一个] [b1; ...; bn] 是 [(a1,b1); ...; (an,bn)]。

如果两个列表的长度不同,则引发 Invalid_argument。不是尾递归的。

【讨论】:

【参考方案2】:

除了提到的其他解决方案之外,从 ocaml-4.08 开始,您可以提供 let+ 和 and+ 运算符,它们将按总和方式压缩列表,否则您可能会考虑使用应用程序。是不是对它们的改进在旁观者的眼中:

let (let+) list f = List.map f list

let (and+) a b =
  let rec loop first second =
    match first,second with
      first_hd::first_tl,second_hd::second_tl ->
       (first_hd,second_hd)::(loop first_tl second_tl)
    | _ -> []
  in
  loop a b

let pairlists = function
    first,second ->
     let+ elt1 = first
     and+ elt2 = second in
     [elt1 ; elt2]

(* example *)
let () =
  let res = pairlists ([1;2;3], [4;5;6]) in
  List.iter
    (fun list -> List.iter (fun i -> Printf.printf "%d " i) list ;
                 print_endline "")
    res

如果您使用的是应用程序,这里作为比较是更传统的方法

let pure x = [x]

let (<*>) aps args =
  List.concat (List.map (fun f -> List.map (fun x -> f x) args) aps)

let (<|>) aps args =
  let rec loop args_rest aps_rest =
    match args_rest,aps_rest with
      args_hd::args_tl,aps_hd::aps_tl ->
       (aps_hd args_hd)::(loop args_tl aps_tl)
    | _ -> []
  in
  loop args aps

let pairlists = function
    first,second ->
     let two_list a b = a :: [b] in
     pure two_list <*> first <|> second

(* example *)
let () =
  let res = pairlists ([1;2;3], [4;5;6]) in
  List.iter
    (fun list -> List.iter (fun i -> Printf.printf "%d " i) list ;
                 print_endline "")
    res

【讨论】:

【参考方案3】:

如果您的结果列表应该包含由两个子列表的元素组成的元素,那么您显然必须在每次迭代中解构每个子列表。

如果保证列表具有相同的长度,则解决方案可以很简单:

let rec zip paired_lists =
  match paired_lists with
  | [], [] -> []
  | h1::t1, h2::t2 -> (h1, h2)::(zip (t1, t2))
  | _, _ -> failwith "oops, the lists seems to have different lengths"
;;

zip ([1;2;3], [4;5;6]);;
- : (int * int) list = [(1, 4); (2, 5); (3, 6)]

但是这个不是尾递归的,这显然不好。第二个次优的事情是在每次迭代时重建列表元组(我是 OCaml 的新手,所以编译器很可能足够聪明,可以避免不必要的分配,但仍然......)。修复这两个缺陷也很简单:

let zip_tr paired_lists =
  let list1, list2 = paired_lists in
  let rec aux l1 l2 acc =
    match l1, l2 with
    | [], [] -> List.rev acc
    | h1::t1, h2::t2 -> aux t1 t2 (h1, h2)::acc
    | _, _ -> failwith "oops, the lists seems to have different lengths"
  in aux list1 list2 []
;;

zip_tr ([1;2;3], [4;5;6]);;
- : (int * int) list = [(1, 4); (2, 5); (3, 6)]

【讨论】:

【参考方案4】:

您的代码的签名与预期的签名不匹配:

line 2, characters 11-13:
Warning 26: unused variable l2.
Line 2, characters 7-9:
Warning 26: unused variable l1.
val pairlists : 'a list * 'a list -> 'a list = <fun>

确实,两个可能的匹配都返回 'a list(这是 l2)或 x::zip...,它也是 'a 类型的列表。

你的代码中应该有 (x,y)::list 这样的东西。

另外,pairlists 不是递归的,也不需要这样声明,只有zip 是递归的。 你的函数的结尾应该是这样的(否则 zip 无效):

....
let rec zip (l1,l2) =
match l1 with 
|[] -> l2
|x :: xs -> x :: zip(l2, xs) in
zip twolists ;;

【讨论】:

如果我希望配对列表是递归的,我将如何构建我的代码? (我不一定要使用 zip 功能)。 zip 就足够了,只需将其重命名为pairlists 即可(您的情况不需要辅助功能)。

以上是关于如何使用 OCaml 将两个列表中的每个单独元素压缩到一个列表中的主要内容,如果未能解决你的问题,请参考以下文章

在 OCaml 中访问 (int * float) 列表中的浮点数

如何在ocaml中将字符串转换为整数列表?

无法将新数据合并到列表的每个数据框元素

将哈希表转换为 OCaml 中的对列表(键、值)

Python - 如何按每个列表中的第四个元素对列表列表进行排序? [复制]

OCaml和OCaml评估模型中的功能应用列表