对子列表的操作

时间:2018-05-18 06:52:04

标签: list recursion f#

我目前想知道根据给定标准拆分子列表中的列表的方法。由于这项工作的教学目的,我不使用内置函数。 IE,下面的程序应该给出一个列表,返回一个列表列表,其中每个子列表没有重复项并按升序排列:

increment [4;4;10;20;5;30;6;10] = [[4;10;20];[5;30];[6;10]]
increment [5;6;4;3;2;1] = [[5;6];[4];[3];[2];[1]]

到目前为止,我最好的尝试是基于我制作的这一大块代码:

let rec increment li [lo] = 
    match li with
        |[]         ->  [lo]
        |[x]        ->  [x]::[lo]
        |x::y::xs   ->  if x = y then
                            increment (y::xs) [lo]
                        elif x < y then
                            x::(increment (y::xs) [lo])
                        else
                            (x::(increment xs [lo]))::[lo]

不幸的是,我无法创建列表列表。原则是正确的。它基于我构建的函数,正确隔离升序列表(如果存在):

let rec incrementAux li = 
    match li with
        |[]         ->  []
        |[x]        ->  [x]
        |x::y::xs   ->  if x = y then
                            incrementAux (y::xs)
                        elif x < y then
                            x::(incrementAux (y::xs))
                        else
                            x::(incrementAux [])

任何建议都将受到高度赞赏!

2 个答案:

答案 0 :(得分:3)

如果您想在不使用List模块上的内置函数的情况下执行此操作(纯粹作为学习练习),那么您只需了解mapfold即可你可以自己实现它们。我会从this wikipedia article开始。方便的是,您可以根据rev轻松实施fold,这样就不会有问题。一旦你理解了每个函数的作用,就可以自己实现它们:

let rec fold f state = function
| [] -> state
| head::tail -> fold f (f state head) tail

let map f list =
    [for item in list -> f item]

let rev list = 
    fold (fun acc cur -> cur::acc) [] list

然后,您可以在Szer的解决方案中用自己的函数替换内置函数:

let input = [4;4;10;20;5;30;6;10]
let output = [[4;10;20];[5;30];[6;10]]

let increment = 
    fold (fun (last::rest as total) x ->
        match last with
        | [] -> [x] :: rest
        | h::_ as last ->
        if x > h then (x::last)::rest
        else if x = h then total
        else [x]::total) [[]]
    >> map rev
    >> rev

let testOutput = increment input
testOutput = output

请注意,fold的此实现与F#List的实现方式不同。这是基于维基百科文章中的简单Haskell示例。功能是相同的,但实现是完全不同的,因为F#实际上使用可变累加器和for循环。

答案 1 :(得分:1)

你可以在没有递归的情况下做到这一点。带有一点记忆的List.fold可以提供帮助:

let input = [4;4;10;20;5;30;6;10]
let output = [[4;10;20];[5;30];[6;10]]

let increment = 
    List.fold (fun (last::rest as total) x ->
        match last with
        | [] -> [x] :: rest
        | h::_ as last ->
        if x > h then (x::last)::rest
        else if x = h then total
        else [x]::total) [[]]
    >> List.map List.rev
    >> List.rev

let testOutput = increment input
testOutput = output // true