Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
List 在联合类型的F#列表上操作_List_F#_Discriminated Union - Fatal编程技术网

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