[英]Operating on an F# List of Union Types
这是我在F#联盟类型列表中的问题的延续。 感谢有用的反馈,我能够创建一个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
对象并根据每个对象执行操作。 该操作是,除了应对两种情况中的相同Detail.Divisions
或Summary.Office
。 显然,我必须以不同的方式处理这些问题。 但是我不想复制处理每个类似State
和Sections
的所有代码。
我的第一个(工作)想法如下:
for report in reports do
let mutable isDetail = false
let mutable isSummary = false
match report with
| Detail _ -> isDetail <- true
| Summary _ -> isSummary <- true
...
这会给我一个方法来知道什么时候处理Detail.Divisions
而非Summary.Office
。 但它并没有给我一个可以使用的对象。 我仍然坚持使用report
,不知道它是什么, Detail
或Summary
,也无法访问属性。 我想转换report
,以适当的Detail
或Summary
,然后使用相同的代码来处理这两种情况下,与外Detail.Divisions
和Summary.Office
。 有没有办法做到这一点?
谢谢。
你可以这样做:
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
@kvb的答案可能是我使用的方法,如果我有你描述的数据结构。 但是,我认为考虑您拥有的数据类型是否是最佳表示是有意义的。
事实上, Detail
和Summary
共享两个属性( State
和Sections
)可能意味着Report
某些共同部分是共享的,无论报告的类型如何(如果报告详细,报告可以添加Divisions
或只是Office
,如果是摘要)。
使用以下内容可以更好地表达类似的内容( Section
保持不变,因此我没有将其包含在代码段中):
type ReportInformation =
| Divisions of string list
| Office of string
type Report =
{ State : string;
Sections : Section list
Information : ReportInformation }
如果您使用此样式,则只需访问report.State
和report.Sections
(执行处理的常见部分),然后您可以匹配report.Information
以执行处理的不同部分。
编辑 -回答Jeff的评论 - 如果数据结构已经修复,但视图已更改,您可以使用F# 活动模式编写“适配器”,使用上面描述的视图提供对旧数据结构的访问:
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#活动模式如何提供允许您隐藏实现细节的抽象的一个很好的示例。
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
当您匹配并使用单独的函数处理Divisions
或Office
值时,您可以在每个联合案例中的Detail
或Summary
记录上进行模式匹配,例如
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)
您还可以筛选Detail
和Summary
以便在不同的功能中单独处理它们:
let getDetails reports =
List.choose (function Detail d -> Some d | _ -> None) reports
let getSummaries reports =
List.choose (function Summary s -> Some s | _ -> None) reports
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.