繁体   English   中英

在F#联合类型列表上运行

[英]Operating on an F# List of Union Types

这是我在F#联盟类型列表中的问题的延续。 感谢有用的反馈,我能够创建一个Report列表, 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

既然我已经在一个名为reports的变量中获得了Report的列表,我想迭代这些Report对象并根据每个对象执行操作。 该操作是,除了应对两种情况中的相同Detail.DivisionsSummary.Office 显然,我必须以不同的方式处理这些问题。 但是我不想复制处理每个类似StateSections的所有代码。

我的第一个(工作)想法如下:

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 ,不知道它是什么, DetailSummary ,也无法访问属性。 我想转换report ,以适当的DetailSummary ,然后使用相同的代码来处理这两种情况下,与外Detail.DivisionsSummary.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的答案可能是我使用的方法,如果我有你描述的数据结构。 但是,我认为考虑您拥有的数据类型是否是最佳表示是有意义的。

事实上, DetailSummary共享两个属性( StateSections )可能意味着Report某些共同部分是共享的,无论报告的类型如何(如果报告详细,报告可以添加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#活动模式如何提供允许您隐藏实现细节的抽象的一个很好的示例。

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

当您匹配并使用单独的函数处理DivisionsOffice值时,您可以在每个联合案例中的DetailSummary记录上进行模式匹配,例如

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)

您还可以筛选DetailSummary以便在不同的功能中单独处理它们:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM