4

这是我在F# List of Union Types上的问题的延续。多亏了有用的反馈,我能够创建一个Reports 列表,其中Report之一是DetailSummary。这是数据定义再次:

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

现在我已经Report在一个名为 的变量中获得了 s的列表reports,我想遍历这些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为适当的DetailorSummary然后使用相同的代码来处理任何一种情况,除了Detail.Divisionsand Summary.Office。有没有办法做到这一点?

谢谢。

4

5 回答 5

6

你可以这样做:

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
于 2012-12-10T16:37:42.207 回答
4

如果我有你描述的数据结构,@kvb 的答案可能是我会使用的方法。但是,我认为考虑您拥有的数据类型是否是最好的表示是有意义的。

两者DetailSummary共享两个属性 (StateSections) 的事实可能意味着,Report无论报告的类型如何,都可以共享 a 的某些共同部分(并且报告可以添加Divisions,如果它是详细的,或者只是Office如果是摘要) .

使用以下内容可以更好地表达类似的内容(Section保持不变,因此我没有将其包含在代码段中):

type ReportInformation = 
  | Divisions of string list
  | Office of string

type Report = 
  { State       : string;
    Sections    : Section list 
    Information : ReportInformation }

如果你使用这种风格,你可以只访问report.Statereport.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# 活动模式如何提供允许您隐藏实现细节的抽象的一个很好的示例。

于 2012-12-10T22:37:21.110 回答
3

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
于 2012-12-10T16:50:12.607 回答
2

当您使用单独的函数匹配和处理or值时,您可以在每个联合案例中对Detailor记录进行模式匹配,例如SummaryDivisionsOffice

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
于 2012-12-10T16:38:03.623 回答
2

您可以重构代码以使每个公共字段都有一个实用函数并使用嵌套模式匹配:

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
于 2012-12-10T16:48:07.950 回答