4

これは、 F# List of Union Typesでの私の質問の続きです。有益なフィードバックのおかげで、または のいずれかでReportある のリストを作成することができました。もう一度データ定義を次に示します。ReportDetailSummary

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という変数に のリストを取得したので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適切なDetailorに変換しSummary、同じコードを使用していずれかのケースを処理したいと思いますが、 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による答えは、おそらく、あなたが説明したデータ構造を持っている場合に使用するアプローチです。ただし、あなたが持っているデータ型が可能な限り最良の表現であるかどうかを考えるのは理にかなっていると思います.

と の両方が 2 つのプロパティ (と)Detailを共有しているという事実は、おそらく、レポートの種類に関係なく共有される a の共通部分があることを意味します (レポートが詳細である場合、または要約である場合にのみレポートに追加できます) 。 .SummaryStateSectionsReportDivisionsOffice

そのようなものは、次のように表現する方が適切です (Sectionは同じままなので、スニペットには含めませんでした)。

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

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

このスタイルを使用すると、(処理の共通部分を実行するために) and にアクセスするだけreport.Stateで、処理のさまざまな部分を実行するためにreport.Sections一致することができます。report.Information

編集 -ジェフのコメントへの回答 - データ構造が既に修正されているが、ビューが変更されている場合、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

最初のアクティブなパターンは常に成功し、共通部分を抽出します。2 番目の方法では、2 つのケースを区別できます。次に、次のように記述できます。

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

コードをリファクタリングして、共通フィールドごとにユーティリティ関数を設定し、ネストされたパターンマッチングを使用できます。

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 に答える
2

または値を別の関数で照合して処理する場合、各共用体ケースのDetailまたはレコードでパターン マッチを行うことができます。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 に答える