[英]Operating on an F# List of Union Types
這是我在F#聯盟類型列表中的問題的延續。 感謝有用的反饋,我能夠創建一個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
對象並根據每個對象執行操作。 該操作是,除了應對兩種情況中的相同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
,以適當的Detail
或Summary
,然后使用相同的代碼來處理這兩種情況下,與外Detail.Divisions
和Summary.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的答案可能是我使用的方法,如果我有你描述的數據結構。 但是,我認為考慮您擁有的數據類型是否是最佳表示是有意義的。
事實上, Detail
和Summary
共享兩個屬性( State
和Sections
)可能意味着Report
某些共同部分是共享的,無論報告的類型如何(如果報告詳細,報告可以添加Divisions
或只是Office
,如果是摘要)。
使用以下內容可以更好地表達類似的內容( Section
保持不變,因此我沒有將其包含在代碼段中):
type ReportInformation =
| Divisions of string list
| Office of string
type Report =
{ State : string;
Sections : Section list
Information : ReportInformation }
如果您使用此樣式,則只需訪問report.State
和report.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
當您匹配並使用單獨的函數處理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
您可以重構代碼以使每個公共字段具有實用程序功能並使用嵌套模式匹配:
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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.