如何解决如何折叠每张列表的x个元素
所以,让我们说我们有一些如下列表:[1; 2; 3; 4; 5; 6]
,并且让我们说我想在函数的每次调用中折叠2个元素。
因此,我将依次在(1,2)
,(3,4)
和(5,6)
上应用该函数。
这是我尝试执行的功能:
let fold_left_multiple (func: 'a -> 'b list -> 'a) (base: 'a) (lst: 'b list) (items_per_fold: int): 'a * 'b list =
let (acc,remainder,_) = List.fold_left (fun (acc,cur_fold_acc,cur_num) el ->
if cur_num mod items_per_fold = 0 then (func acc (List.rev (el::cur_fold_acc)),[],1)
else (acc,el::cur_fold_acc,cur_num + 1)
) (base,1) lst in (acc,remainder)
这有点奏效;但是,这样做的问题是在函数中使用这些元素并不容易。
我的首选实现将以某种方式使用元组或数组来简化元素访问。
下面是一个更好的输入/输出示例(使用utop
语法)。在这种情况下,我要总结每对元素。
# fold_left_multiple (fun lst (e1,e2,e3) -> (e1 + e2 + e3)::lst) [] [1; 2; 3; 4; 5; 6; 7; 8] 3;;
- : int list * int list = ([15; 6],[7; 8])
在这里,如果列表的长度不能被n
整除,则其余元素将放入元组的第二个元素。
(我不介意在解决方案中是否可以逆转此余数。)
解决方法
如果存在已知且数量有限的插槽,则可以方便使用。但是一旦不是这种情况,他们的确会变得笨拙。因此,我认为让文件夹功能接收输入列表的子列表并没有错。
获取函数语言中前n个(或更少)元素的常用方法是
函数take
的含义。分别删除前n个元素(或更少的n个元素)的常用方法分别是使用名为drop
的函数。
借助这两个功能,可以像这样实现所需的功能:
(* take and drop seem to be missing in ocamls half full batteries...
maybe because it is not idiomatic or efficient or both...
*)
let take n lst =
let rec loop acc n l =
match n with
| 0 -> List.rev acc
| x ->
match l with
| [] -> List.rev acc
| x::xs -> loop (x::acc) (n-1) (List.tl l) in
loop [] n lst
let drop n lst =
let rec loop n l =
match n with
| 0 -> l
| _ ->
match l with
| [] -> l
| _::_ -> loop (n-1) (List.tl l) in
loop n lst
let fold_windowed folder wsize acc lst =
let rec loop acc l =
match l with
| [] -> List.rev acc
| _::_ ->
loop (folder acc (take wsize l)) (List.tl l) in
loop acc lst
在F#中我曾经使用过但在Ocaml中找不到开箱即用的一些附加功能的帮助下,您可以按以下方式使用fold_windowed
:
let id x = x (* ocaml should have that right out of the box... *)
(* shamelessly derived from F# List.init,with the diff,that the name initializer
seems to be reserved in ocaml,hence the somewhat silly name 'initor'
*)
let list_init n initor =
let rec loop acc i =
match i with
| 0 -> acc
| _ -> loop ((initor i)::acc) (i-1) in
loop [] n
,
# fold_windowed (fun acc l -> l::acc) 3 [] (list_init 10 id);;
_:int列表列表=
[[1; 2; 3]; [2; 3; 4]; [3; 4; 5]; [4; 5; 6]; [5; 6; 7]; [6; 7; 8]; [7; 8; 9];
[8; 9; 10]; [9; 10]; [10]]
您可以修改标准fold_left
函数以对多个元素进行操作。这是一对可以配对的东西:
let rec fold_left_2 f acc l =
match l with
| a::b::rest -> fold_left_2 f (f acc a b) rest
| remainder -> (acc,remainder)
编辑:修改为按要求返回余数,而不是忽略它。
为说明我的观点,将其推广到任意数量的元素是可能的,但不是很有益,这是一个实现,它允许使用split函数任意分割输入列表:
let rec fold_left_n splitf f acc l =
match splitf l with
| None,remainder -> (acc,remainder)
| Some x,rest -> fold_left_n splitf f (f acc x) rest
及其示例调用:
fold_left_n
(function a::b::c::rest -> (Some (a,b,c),rest) | remainder -> (None,remainder))
(fun lst (e1,e2,e3) -> (e1 + e2 + e3)::lst) [] [1; 2; 3; 4; 5; 6; 7; 8];;
类似地,可以编写一个函数来提取任意长度的子列表,而我并没有去实现它,但是它的调用看起来像这样:
fold_left_n 3
(fun lst -> function
| [e1,e3] -> (e1 + e2 + e3)::lst
| _ -> lst (* we assume we're getting a 3-element list,but the compiler doesn't know that so we need to ignore everything else *)
) [] [1; 2; 3; 4; 5; 6; 7; 8];;
它们使用起来非常复杂且冗长,与仅编写专门的实现相比并没有什么好处。
,这可能有助于确定您希望函数具有哪种类型。
即使所有元素都是整数,也没有类型可以表示具有不同数量元素的元组。每个元素的数量都是不同的类型:int * int
,int * int * int
等。
如果要编写通用函数,则折叠后的函数将需要获取除元组以外的其他形式的输入(也许是列表)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。