[英]Operating on an F# List of Union Types
This is a continuation of my question at F# List of Union Types . 这是我在F#联盟类型列表中的问题的延续。 Thanks to the helpful feedback, I was able to create a list of
Report
s, with Report
being either Detail
or Summary
. 感谢有用的反馈,我能够创建一个
Report
列表, Report
可以是Detail
或Summary
。 Here's the data definition once more: 这是数据定义了:
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
Now that I've got the list of Report
s in a variable called reports
, I want to iterate over those Report
objects and perform operations based on each one. 既然我已经在一个名为
reports
的变量中获得了Report
的列表,我想迭代这些Report
对象并根据每个对象执行操作。 The operations are the same except for the cases of dealing with either Detail.Divisions
or Summary.Office
. 该操作是,除了应对两种情况中的相同
Detail.Divisions
或Summary.Office
。 Obviously, I have to handle those differently. 显然,我必须以不同的方式处理这些问题。 But I don't want to duplicate all the code for handling the similar
State
and Sections
of each. 但是我不想复制处理每个类似
State
和Sections
的所有代码。
My first (working) idea is something like the following: 我的第一个(工作)想法如下:
for report in reports do
let mutable isDetail = false
let mutable isSummary = false
match report with
| Detail _ -> isDetail <- true
| Summary _ -> isSummary <- true
...
This will give me a way to know when to handle Detail.Divisions
rather than Summary.Office
. 这会给我一个方法来知道什么时候处理
Detail.Divisions
而非Summary.Office
。 But it doesn't give me an object to work with. 但它并没有给我一个可以使用的对象。 I'm still stuck with
report
, not knowing which it is, Detail
or Summary
, and also unable to access the attributes. 我仍然坚持使用
report
,不知道它是什么, Detail
或Summary
,也无法访问属性。 I'd like to convert report
to the appropriate Detail
or Summary
and then use the same code to process either case, with the exception of Detail.Divisions
and Summary.Office
. 我想转换
report
,以适当的Detail
或Summary
,然后使用相同的代码来处理这两种情况下,与外Detail.Divisions
和Summary.Office
。 Is there a way to do this? 有没有办法做到这一点?
Thanks. 谢谢。
You could do something like this: 你可以这样做:
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
The answer by @kvb is probably the approach I would use if I had the data structure you described. @kvb的答案可能是我使用的方法,如果我有你描述的数据结构。 However, I think it would make sense to think whether the data types you have are the best possible representation.
但是,我认为考虑您拥有的数据类型是否是最佳表示是有意义的。
The fact that both Detail
and Summary
share two of the properties ( State
and Sections
) perhaps implies that there is some common part of a Report
that is shared regardless of the kind of report (and the report can either add Divisions
if it is detailed or just Office
if if is summary). 事实上,
Detail
和Summary
共享两个属性( State
和Sections
)可能意味着Report
某些共同部分是共享的,无论报告的类型如何(如果报告详细,报告可以添加Divisions
或只是Office
,如果是摘要)。
Something like that would be better expressed using the following ( Section
stays the same, so I did not include it in the snippet): 使用以下内容可以更好地表达类似的内容(
Section
保持不变,因此我没有将其包含在代码段中):
type ReportInformation =
| Divisions of string list
| Office of string
type Report =
{ State : string;
Sections : Section list
Information : ReportInformation }
If you use this style, you can just access report.State
and report.Sections
(to do the common part of the processing) and then you can match on report.Information
to do the varying part of the processing. 如果您使用此样式,则只需访问
report.State
和report.Sections
(执行处理的常见部分),然后您可以匹配report.Information
以执行处理的不同部分。
EDIT - In answer to Jeff's comment - if the data structure is already fixed, but the view has changed, you can use F# active patterns to write "adaptor" that provides access to the old data structure using the view that I described above: 编辑 -回答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
The first active pattern always succeeds and extracts the common part. 第一个活动模式总是成功并提取公共部分。 The second allows you to distinguish between the two cases.
第二个允许您区分这两种情况。 Then you can write:
然后你可以写:
let processReport report =
let (Report(state, sections)) = report
// Common processing
match report wiht
| Divisions divs -> // Divisions-specific code
| Office ofc -> // Offices-specific code
This is actually an excellent example of how F# active patterns provide an abstraction that allows you to hide implementation details. 这实际上是F#活动模式如何提供允许您隐藏实现细节的抽象的一个很好的示例。
kvb's answer is good, and probably what I would use. kvb的答案很好,可能就是我会用的。 But the way you've expressed your problem sounds like you want classic inheritance.
但是你表达问题的方式听起来像你想要经典继承。
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
Then you can do precisely what you expect: 然后你可以完全按照你的期望做到:
for report in reports do
match report with
| :? Detail as detail -> //use detail.Divisions
| :? Summary as summary -> //use summary.Office
//use common properties
You can pattern match on the Detail
or Summary
record in each of the union cases when you match and handle the Divisions
or Office
value with a separate function eg 当您匹配并使用单独的函数处理
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
You can refactor the code to have a utility function for each common field and use nested pattern matching: 您可以重构代码以使每个公共字段具有实用程序功能并使用嵌套模式匹配:
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)
You can also filter Detail
and Summary
to process them separately in different functions: 您还可以筛选
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.