Насколько я знаю, не существует простой способ сделать это, но это, безусловно, хороший способ практиковать функциональные навыки программирования. Если вы использовали некоторое иерархическое представление данных (например, XML или JSON), ситуация была бы намного проще, потому что вам не пришлось бы преобразовывать структуру данных из линейных (например, list/array) в иерархическую (в этом случае список списков).
В любом случае, хороший способ подойти к проблеме состоит в том, чтобы понять, что вам нужно выполнить более общую операцию с данными - вам нужно сгруппировать смежные элементы массива, начиная новую группу, когда вы найдете строку с значение в первом столбце.
Начну, добавив номер строки в массив, а затем преобразовать его в список (который, как правило, легче работать в F #):
let data = lines |> Array.mapi (fun i l ->
i, l.Split(';')) |> List.ofSeq
Теперь мы можем написать многоразовую функцию, группы смежных элементов списка и начинает новую группу каждый раз, когда указанный предикат возвращает f
true
:
let adjacentGroups f list =
// Utility function that accumulates the elements of the current
// group in 'current' and stores all groups in 'all'. The parameter
// 'list' is the remainder of the list to be processed
let rec adjacentGroupsUtil current all list =
match list with
// Finished processing - return all groups
| [] -> List.rev (current::all)
// Start a new group, add current to the list
| x::xs when f(x) ->
adjacentGroupsUtil [x] (current::all) xs
// Add element to the current group
| x::xs ->
adjacentGroupsUtil (x::current) all xs
// Call utility function, drop all empty groups and
// reverse elements of each group (because they are
// collected in a reversed order)
adjacentGroupsUtil [] [] list
|> List.filter (fun l -> l <> [])
|> List.map List.rev
Теперь, реализуя свой специфический алгоритм относительно легко. Сначала нужно сгруппировать элементы, начиная новую группу каждый раз, когда первый столбец имеет некоторое значение:
let groups = data |> adjacentGroups (fun (ln, cells) -> cells.[0] <> "")
На втором этапе, нам нужно сделать некоторую обработку для каждой группы. Беру первый элемент (и выбрать название группы), а затем найти минимальное и максимальное число линий среди остальных элементов:
groups |> List.map (fun ((_, firstCols)::lines) ->
let lineNums = lines |> List.map fst
firstCols.[0], List.min lineNums, List.max lineNums)
Обратите внимание, что шаблон согласования в лямбда-функции выдаст предупреждение, но мы можем смело игнорировать это, потому что группа всегда будет непустой.
Резюме: Этот ответ показывает, что если вы хотите, чтобы написать элегантный код, вы можете реализовать многоразовую функцию высшего порядка (например, adjacentGroups
), потому что не все доступно в # основных библиотеках F. Если вы используете функциональные списки, вы можете реализовать его с помощью рекурсии (для массивов вы должны использовать императивное программирование, как в ответе gradbot). Когда у вас есть хороший набор функций многократного использования, большинство проблем легко :-).
Отлично! Это то, что мне нужно. Благодарю. – Max