F# построить список / массив значений + последовательные дубликаты
Мне нужно упаковать данные вот так:
let data = [1; 2; 2; 3; 2; 2; 2; 4]
let packed = [(1, 1); (2, 2); (3, 1); (2, 3); (4, 1)]
Где каждый элемент говорит, сколько раз он существует до следующего. Однако он должен работать с несмежными дублированиями.
Я могу работать с классическим императивным кодом, но интересно, как это сделать функционально.
Также, Seq.countBy не работает, потому что он учитывает все значения
3 ответов:
Если у вас уже есть императивная версия, вы можете выполнить ряд небольших шагов, чтобы перейти к рекурсивной реализации.
Рекурсия
Хотя я не знаю, как выглядит ваша императивная версия, вот рекурсивная версия:
let pack xs = let rec imp acc = function | [] -> acc | h::t -> match acc with | [] -> imp [(h, 1)] t | (i, count) :: ta -> if h = i then imp ((i, count + 1) :: ta) t else imp ((h, 1) :: (i, count) :: ta) t xs |> imp [] |> List.revЭта функция имеет тип
'a list -> ('a * int) list when 'a : equality. Он использует частную "функцию реализации", называемуюimpдля выполнения работы. Эта функция рекурсивна и пронизывает накопитель (называемыйacc) на всем протяжении. Этот аккумулятор является список результатов, имеющий вид('a * int) list.Если список накопителей пуст, то глава исходного списка (
h), а также счетчик1, создаются как кортеж как единственный элемент обновленного накопителя, и функцияimpрекурсивно вызывается с этим обновленным накопителем.Если накопитель уже содержит хотя бы один элемент, элемент извлекается путем сопоставления с шаблоном, и элемент в этом кортеже (
i) сравнивается сh. Еслиh = i, то аккумулятор обновляется; в противном случае новый Кортеж сохраняется наacc. В обоих случаях, однако,impрекурсивно вызывается с новым аккумулятором.Вы можете вызвать его с помощью списка , эквивалентного вашему оригинальному кортежу, например:
> pack [1; 2; 2; 3; 2; 2; 2; 4];; val it : (int * int) list = [(1, 1); (2, 2); (3, 1); (2, 3); (4, 1)]Сложить
Если у вас есть рекурсивная версия,у вас часто есть рецепт для версии, использующей сгиб. В этом случае, поскольку указанная выше функция
packдолжна в конце концов обратить аккумулятор (используяList.rev), A правая складка наиболее подходящий. В F# это делается с помощью встроенной функцииList.foldBack:В этом случае функция, переданная вlet pack' xs = let imp x = function | (i, count) :: ta when i = x -> (i, count + 1) :: ta | ta -> (x, 1) :: ta List.foldBack imp xs []List.foldBack, слишком сложна, чтобы передать ее как анонимную функцию, поэтому я решил определить ее как частную внутреннюю функцию. Это эквивалентно рекурсивной функцииimp, используемой вышеупомянутой функциейpack, но вы не заметите, что она не должна вызывать себя рекурсивно. Вместо этого он просто должен вернуть новое значение для аккумулятора.Результатом является то же самое:
> pack' [1; 2; 2; 3; 2; 2; 2; 4];; val it : (int * int) list = [(1, 1); (2, 2); (3, 1); (2, 3); (4, 1)]
Мое решение предполагает, что коллекция
data- это список. Если иметь его в виде кортежа (в соответствии с вашим примером) было намеренно, то для того, чтобы мое решение работало, кортеж должен быть преобразован в список (пример, как это сделать, можно найти здесь).let groupFunc list = let rec groupFuncRec acc lst init count = match lst with | [] -> List.rev acc | head::[] when head = init -> groupFuncRec ((init, count)::acc) [] 0 0 | head::[] when head <> init -> groupFuncRec ((head, 1)::acc) [] 0 0 | head::tail when head = init -> groupFuncRec acc tail head (count+1) | head::tail when head <> init -> groupFuncRec ((init, count)::acc) tail head 1 let t = List.tail list let h = List.head list groupFuncRec [] t h 1Когда я запускаю функцию на ваших образцах данных, я получаю ожидаемый результат:
list = [(1, 1); (2, 2); (3, 1); (4, 1)]
Вы можете заставить
Seq.countByработать, включив некоторую позиционную информацию в его аргумент. Конечно, вам нужно затем сопоставить свои исходные данные.[1; 2; 2; 3; 2; 2; 2; 4] |> Seq.scan (fun (s, i) x -> match s with | Some p when p = x -> Some x, i | _ -> Some x, i + 1 ) (None, 0) |> Seq.countBy id |> Seq.choose (function | (Some t, _), n -> Some(t, n) | _ -> None ) |> Seq.toList // val it : (int * int) list = [(1, 1); (2, 2); (3, 1); (2, 3); (4, 1)]
Comments