簡體   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