Разделение списка элементов на два списка нечетных и четных проиндексированных элементов - PullRequest
15 голосов
/ 30 октября 2011

Я хотел бы создать функцию, которая принимает список и возвращает два списка: первый содержит каждый нечетный элемент, а второй содержит каждый четный элемент.

Например, учитывая [1;2;4;6;7;9], я быхотел бы вернуть [ [1;4;7] ; [2;6;9] ].

Я написал это до сих пор, и я не знаю, как прогрессировать.

let splitList list =
    let rec splitOdd oList list1 list2 =
        match oList with
        | [] -> []
        | head :: tail -> splitEven tail (list1::head) list2
    and splitEven oList list1 list2 =
        match oList with
        | [] -> []
        | head :: tail -> splitOdd tail list1 (list2::head)
    splitOdd list [] []

Ответы [ 8 ]

39 голосов
/ 30 октября 2011

Реализация, которая не переполняет стек:

let splitList list = List.foldBack (fun x (l,r) -> x::r, l) list ([],[])
11 голосов
/ 30 октября 2011

Если вы подразумеваете нечетные и четные значения для позиций элементов, вот (нерекурсивное) решение:

let rec splitList = function
    | [] -> [], []
    | [x]-> [x], []
    | x1::x2::xs -> let xs1, xs2 = splitList xs
                    x1::xs1, x2::xs2
6 голосов
/ 30 октября 2011

Вот простое нерекурсивное решение:

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

Применительно к вашему образцу он дает именно то, что вы хотели:

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

val it : int list list = [[1; 4; 7]; [2; 6; 9]]
2 голосов
/ 30 октября 2011

Похоже, это то, к чему вы стремились, и это действительно хороший способ сделать это, поскольку он хвостовой рекурсивен.

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
2 голосов
/ 30 октября 2011

Еще одна (менее эффективная) опция

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 ]

Если вы хотите избежать создания временных списков, рассмотрите возможность использования последовательностей:

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

Еще одна, похожая на версию Дэниела:

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
1 голос
/ 09 марта 2014

Мои 2 ¢ в OCaml, так как награда еще открыта.

Может быть, вы могли бы намекнуть, что вы хотите. Elegance? FP? Хвостовая рекурсия? Производительность

Edit:

Я удалил более длинное решение. Для работы List.partition предикат отсутствовал. Вот оно:

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

Улучшения есть? Тестирование решения:

# so_split [1;2;4;6;7;9];;
- : int list * int list = ([1; 4; 7], [2; 6; 9])
1 голос
/ 30 октября 2011

Звучит так, как вы хотите List.partition <'T> (http://msdn.microsoft.com/en-us/library/ee353782.aspx). Эта функция принимает предикат и список и возвращает пару (2-кортеж), где первый элемент - это все элементы, прошедшие тест, а второй - все элементы, не прошедшие тест Таким образом, вы можете классифицировать шансы и четности с помощью:

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

Если вы действительно хотите получить список, вы можете использовать fst и snd, чтобы извлечь элементы из кортежа и поместить их в список.

Удачи!

0 голосов
/ 12 марта 2014

Просто для полноты вот скучное, более настоятельное решение:

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 
...