gpt4 book ai didi

f# - 解析分层 CSV 的功能方法

转载 作者:行者123 更新时间:2023-12-05 00:42:10 25 4
gpt4 key购买 nike

我正在尝试创建一段代码,但无法使其正常工作。我能想到的最简单的例子是解析一些 CSV 文件。
假设我们有一个 CVS 文件,但其中的数据是以某种层次结构组织的。像这样:

Section1;
;Section1.1
;Section1.2
;Section1.3
Section2;
;Section2.1
;Section2.2
;Section2.3
;Section2.4

等等。

我这样做了:
let input = 
"a;
;a1
;a2
;a3
b;
;b1
;b2
;b3
;b4
;b5
c;
;c1"

let lines = input.Split('\n')
let data = lines |> Array.map (fun l -> l.Split(';'))

let sections =
data
|> Array.mapi (fun i l -> (i, l.[0]))
|> Array.filter (fun (i, s) -> s <> "")

我得到了
val sections : (int * string) [] = [|(0, "a"); (4, "b"); (10, "c")|]

现在我想为每个部分创建一个行索引范围列表,如下所示:
[|(1, 3, "a"); (5, 9, "b"); (11, 11, "c")|]

第一个数字是小节范围的起始行索引,第二个 - 结束行索引。我怎么做?我正在考虑使用 fold 功能,但无法创建任何东西。

最佳答案

据我所知,没有简单的方法可以做到这一点,但这绝对是练习函数式编程技巧的好方法。如果您使用数据的一些分层表示(例如 XML 或 JSON),情况会容易得多,因为您不必将数据结构从线性(例如列表/数组)转换为分层(在这种情况下,一个列表列表)。

无论如何,解决问题的一个好方法是意识到您需要对数据进行一些更一般的操作 - 您需要对数组的相邻元素进行分组,当您在第一个中找到具有值的行时开始一个新组柱子。

我将首先向数组添加一个行号,然后将其转换为列表(这通常在 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 )

请注意,lambda 函数中的模式匹配会发出警告,但我们可以放心地忽略它,因为该组将始终为非空。

摘要:这个答案表明,如果您想编写优雅的代码,您可以实现可重用的高阶函数(例如 adjacentGroups ),因为并非所有内容都在 F# 核心库中可用。如果您使用功能列表,则可以使用递归来实现它(对于数组,您将使用命令式编程,如 gradbot 的答案)。一旦您拥有一组良好的可重用功能,大多数问题都很容易:-)。

关于f# - 解析分层 CSV 的功能方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2338316/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com