List 在联合类型的F#列表上操作
这是我的问题的继续。多亏了有帮助的反馈,我能够创建一个List 在联合类型的F#列表上操作,list,f#,discriminated-union,List,F#,Discriminated Union,这是我的问题的继续。多亏了有帮助的反馈,我能够创建一个Report列表,其中Report要么是Detail要么是Summary。以下是数据定义: module Data type Section = { Header: string; Lines: string list; Total: string } type Detail = { State: string; Divisio
Report
列表,其中Report
要么是Detail
要么是Summary
。以下是数据定义:
module Data
type Section = { Header: string;
Lines: string list;
Total: string }
type Detail = { State: string;
Divisions: string list;
Sections: Section list }
type Summary = { State: string;
Office: string;
Sections: Section list }
type Report = Detail of Detail | Summary of Summary
现在,我已经在一个名为reports
的变量中获得了Report
的列表,我想迭代那些Report
对象,并基于每个对象执行操作。除处理详细.部门
或摘要.办公室
的情况外,操作相同。显然,我必须以不同的方式处理这些问题。但是我不想复制所有代码来处理类似的状态
和部分
我的第一个(工作)想法如下:
for report in reports do
let mutable isDetail = false
let mutable isSummary = false
match report with
| Detail _ -> isDetail <- true
| Summary _ -> isSummary <- true
...
报告中的报告的
设可变isDetail=false
设可变isSummary=false
匹配报告
|详细信息->isDetail isSummary您可以执行以下操作:
for report in reports do
match report with
| Detail { State = s; Sections = l }
| Summary { State = s; Sections = l } ->
// common processing for state and sections (using bound identifiers s and l)
match report with
| Detail { Divisions = l } ->
// unique processing for divisions
| Summary { Office = o } ->
// unique processing for office
当您使用一个单独的函数(例如)匹配和处理部门
或办公室
值时,您可以在每个联合案例中的细节
或摘要
记录上进行模式匹配
let blah =
for report in reports do
let out = match report with
| Detail({ State = state; Divisions = divisions; Sections = sections } as d) ->
Detail({ d with Divisions = (handleDivisions divisions) })
| Summary({ State = state; Office = office; Sections = sections } as s) ->
Summary( { s with Office = handleOffice office })
//process out
您可以重构代码,使每个公共字段都有一个实用程序函数,并使用嵌套模式匹配:
let handleReports reports =
reports |> List.iter (function
| Detail {State = s; Sections = ss; Divisions = ds} ->
handleState s
handleSections ss
handleDivisions ds
| Summary {State = s; Sections = ss; Office = o} ->
handleState s
handleSections ss
handleOffice o)
您还可以过滤详细信息
和摘要
以在不同的功能中分别处理它们:
let getDetails reports =
List.choose (function Detail d -> Some d | _ -> None) reports
let getSummaries reports =
List.choose (function Summary s -> Some s | _ -> None) reports
kvb的答案很好,可能是我会使用的。但是你表达问题的方式听起来像是想要经典继承
type ReportPart(state, sections) =
member val State = state
member val Sections = sections
type Detail(state, sections, divisions) =
inherit ReportPart(state, sections)
member val Divisions = divisions
type Summary(state, sections, office) =
inherit ReportPart(state, sections)
member val Office = office
然后,您可以完全按照您的期望进行:
for report in reports do
match report with
| :? Detail as detail -> //use detail.Divisions
| :? Summary as summary -> //use summary.Office
//use common properties
@kvb的答案可能是如果我有您描述的数据结构,我会使用的方法。然而,我认为考虑您拥有的数据类型是否是最好的表示形式是有意义的
Detail
和Summary
共享两个属性(State
和Sections
)这一事实可能意味着无论报告的类型如何,都会共享报告的某些公共部分(如果是详细的,报告可以添加部门
,如果是摘要,则只添加办公室
)
类似的内容最好使用以下内容来表达(部分保持不变,因此我没有将其包含在代码段中):
如果使用此样式,则只需访问report.State
和report.Sections
(执行处理的公共部分),然后就可以匹配report.Information
执行处理的不同部分
编辑-回答Jeff的评论-如果数据结构已经固定,但视图已经更改,您可以使用F#active patterns编写“adapter”,使用我上面描述的视图访问旧数据结构:
let (|Report|) = function
| Detail dt -> dt.State, dt.Sections
| Summary st -> st.State, st.Sections
let (|Divisions|Office|) = function
| Detail dt -> Divisions dt.Divisions
| Summary st -> Office st.Office
第一个活动模式始终成功并提取公共部分。第二个模式允许您区分这两种情况。然后您可以编写:
let processReport report =
let (Report(state, sections)) = report
// Common processing
match report wiht
| Divisions divs -> // Divisions-specific code
| Office ofc -> // Offices-specific code
这实际上是一个很好的例子,说明了F#active模式如何提供一个抽象,允许您隐藏实现细节。这是我最终实际使用的技术。我认为我最喜欢Thomas的方法,但我在他做出响应之前就实现了。我的任务基本上是必须完成的(逐行循环打印文件并进行解析),因此我最终得到了一些非常不惯用的F#代码。但这种技术对我很有用。谢谢!受这个答案的启发,我在我的答案中添加了另一个版本。其想法是隐藏这三种模式(可能会在程序中重复使用)在活动模式中。我喜欢这个答案。但出于某种原因,我回避继承。不过,我会在将来的项目中记住它。谢谢你!谢谢你的回答,Tomas。我有一个使用我的数据结构的工作应用程序,然后用户向我提出了一个新的/不同的要求(嘿,让我们使用这些特定办公室的摘要,而不是细节)。我可能应该从零开始使用您描述的数据定义。唉,事后诸葛亮是20/20,我不愿意放弃一个程序,因为对于原始规范来说,它工作得非常完美!啊,这是一件有趣的事情!我添加了一个带有活动模式的示例,可以让您通过啊,一种不同的(也许更合适的)方式。很有趣!谢谢你。我从来没有完全掌握过活动模式。也许现在是时候付出额外的努力了!
let processReport report =
let (Report(state, sections)) = report
// Common processing
match report wiht
| Divisions divs -> // Divisions-specific code
| Office ofc -> // Offices-specific code