Splitting a list of items into two lists of odd and even indexed items

ⅰ亾dé卋堺 提交于 2019-11-28 11:58:12

Implementation which does not stack-overflows:

let splitList list = List.foldBack (fun x (l,r) -> x::r, l) list ([],[])

If you mean odd and even values for positions of items, here is a (non-tail-recursive) solution:

let rec splitList = function
    | [] -> [], []
    | [x]-> [x], []
    | x1::x2::xs -> let xs1, xs2 = splitList xs
                    x1::xs1, x2::xs2

Here is a straightforward non-recursive solution:

let splitList ll =
    ll
    |> List.mapi (fun i x -> (i % 2 = 0, x))
    |> List.partition fst
    |> fun (odd,even) -> [List.map snd odd, List.map snd even];;

val splitList : 'a list -> 'a list list

Being applied to your sample it yields exactly what you wanted:

splitList [1;2;4;6;7;9];;

val it : int list list = [[1; 4; 7]; [2; 6; 9]]

It looks like this is what you were going for, which is indeed a nice way to do it as it's tail-recursive.

let splitList items =
  let rec splitOdd odds evens = function
    | [] -> odds, evens
    | h::t -> splitEven (h::odds) evens t
  and splitEven odds evens = function
    | [] -> odds, evens
    | h::t -> splitOdd odds (h::evens) t
  let odds, evens = splitOdd [] [] items
  List.rev odds, List.rev evens

Another (less efficient) option

let splitList xs = 
    let odd, even =
        xs
        |> List.zip [ 1 .. (List.length xs) ]
        |> List.partition (fun (i, _) -> i % 2 <> 0)
    [ odd |> List.map snd; even |> List.map snd ]

If you want to avoid creating temporary lists, consider using sequences:

let splitListSeq xs =
    xs
    |> Seq.mapi (fun i x -> (i % 2 = 0, x))
    |> Seq.groupBy (fun (b, _) -> b)
    |> Seq.map snd
    |> Seq.map ((Seq.map snd) >> Seq.toList)
    |> Seq.toList

Yet, another one, similar to Daniel's version:

let splitListRec xs =
    let rec loop l r = function
        | []      -> [l; r]
        | x::[]   -> [x::l; r]
        | x::y::t -> loop (x::l) (y::r) t
    loop [] [] xs |> List.map List.rev

Sounds like you want List.partition<'T> ( http://msdn.microsoft.com/en-us/library/ee353782.aspx ). This function takes a predicate and a list and will return a pair (2-tuple) where the first element is all the elements that passed your test and the second is all the elements that did not pass the test. So you could classify odds and evens with:

List.partition odd [1;2;4;6;7;9]

If your really want a list, you can use fst and snd to extract the elements from the tuple and put them in a list.

Good luck!

My 2¢, in OCaml, since there still is a bounty open.

Maybe you could give us a hint what you want. Elegance? FP? Tail recursion? Performance?

Edit:

I removed the longer solution. For List.partition to work, the predicate was missing. Here it is:

let so_split lst = 
  let flag = ref false in
  List.partition (fun e -> flag := not !flag; !flag) lst

Improvements any? Testing the solution:

# so_split [1;2;4;6;7;9];;
- : int list * int list = ([1; 4; 7], [2; 6; 9])

Just for completeness here is a boring, more imperative solution:

let splitList (list:int list) =
    let odds = [for i in 0..list.Length-1 do
                  if i%2=1 then
                    yield list.[i]]
    let evens = [for i in 0..list.Length-1 do
                    if i%2=0 then
                        yield list.[i]]
    odds,evens 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!