Ответ 1
Реализация, которая не переполняет стек:
let splitList list = List.foldBack (fun x (l,r) -> x::r, l) list ([],[])
Я хотел бы создать функцию, которая принимает список и возвращает два списка: первый содержит каждый нечетный элемент, а второй содержит каждый четный элемент.
Например, если задано [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 [] []
Реализация, которая не переполняет стек:
let splitList list = List.foldBack (fun x (l,r) -> x::r, l) list ([],[])
Если вы имеете в виду нечетные и четные значения для позиций элементов, вот (не хвостовое рекурсивное) решение:
let rec splitList = function
| [] -> [], []
| [x]-> [x], []
| x1::x2::xs -> let xs1, xs2 = splitList xs
x1::xs1, x2::xs2
Вот прямое нерекурсивное решение:
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]]
Другая (менее эффективная) опция
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
Похоже, это то, к чему вы стремились, что действительно хороший способ сделать это, поскольку он хвост-рекурсивный.
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
Похоже, вы хотите List.partition < 'T > (http://msdn.microsoft.com/en-us/library/ee353782.aspx). Эта функция принимает предикат и список и возвращает пару (2-кортеж), где первым элементом являются все элементы, прошедшие ваш тест, а второй - все элементы, которые не прошли тест. Таким образом, вы можете классифицировать коэффициенты и коэффициенты с помощью:
List.partition odd [1;2;4;6;7;9]
Если вам действительно нужен список, вы можете использовать fst
и snd
, чтобы извлечь элементы из кортежа и поместить их в список.
Удачи!
My 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])
Просто для полноты здесь есть скучное, более императивное решение:
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