如何使用 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) 列表中的浮点数