问题
I am currently wondering on an approach to split a list in sub-lists according to a given criteria. Because of the didactic purpose of this work, I do not use built-in functions. IE, the following program should, given a list, return a list of lists, where each sub-list does not have duplicates and is in ascending order:
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]]
My best attempt so far is based on this chunk of code I produced:
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]
Unfortunately, I fail in creating the list of lists. The principle is correct. It is based on the function I built, that correctly isolates an ascending list if present:
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 [])
Any suggestion would be highly appreciated!
回答1:
If you want to do this without using the built-in functions on the List
module (purely as a learning exercise), then you just need to understand map
and fold
so you can implement them yourself. I would start with this wikipedia article. Conveniently, you can easily implement rev
in terms of fold
, so that shouldn't be a problem. Once you understand what each function does, you can implement them yourself like so:
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
Then, you can just substitute your own functions for the built-in functions in Szer's solution:
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
Note, this implementation of fold
is different from how F# List
does it. This is based on the simple Haskell example in the wikipedia article. The functionality is the same, but the implementation is quite different, as F# actually uses a mutable accumulator and a for-loop.
回答2:
You could do it without recursion. List.fold
with a little bit of memory could help:
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
来源:https://stackoverflow.com/questions/50405408/operations-on-sublists