簡體   English   中英

F#中的字典推導(?)(從C#轉換)

[英]Dictionary comprehensions(?) in F# (converting from C#)

好,所以,我剛開始學習F#。 我曾經接觸過大學等提供的功能性語言,但是在使用F#等語言進行現實世界編程時,我還是很環保。

我每天都使用C#工作,但是今天我有機會花一些時間在公司的代碼庫中,並從F#的角度進行研究。 我決定嘗試用F#重寫一些C#代碼,以在現實的業務環境中體會這種語言。

這是我努力翻譯的一些C#代碼的解釋:

// MyData is a class with properties Id, Analysis, and some other relevant properties
// Each pair of (Id, Analysis) is (should be) distinct
IEnumerable<MyData> data = // fetch from DB...

// dataDict[id[analysis]] = MyData object (or "row") from DB
var dataDict = new Dictionary<String, Dictionary<String, MyData>> ();
foreach(var d in data)
{
    if(!dataDict.ContainsKey(d.Id))
        dataDict.Add(d.Id, new Dictionary<string, MyData>());

    if (dataDict[d.Id].ContainsKey(d.Analysis))
    {
        logger.Warn(String.Format("Id '{0}' has more than one analysis of type '{1}', 
            rows will be ignored", d.Id, d.Analysis));
    }
    else
    {
        dataDict[d.Id].Add(d.Analysis, d);
    }
} 

我嘗試以“功能性”方式重寫循環導致了以下代碼,但是我對此並不滿意。

let dataDict = 
      dict [ 
        for d in data 
          |> Seq.distinctBy(fun d -> d.Id) -> d.Id, 
             dict [                                                                                                   
                 for x in data |> Seq.filter(fun a -> a.Id = d.Id) -> x.Analysis, x
             ]
      ]

此代碼有兩個問題:

  • 如果存在重復的(標識,分析)對,則不會記錄警告,甚至更糟
  • 我使用for和Seq.filter處理數據(至少)兩次(至少)。

我該如何改善? 我做錯了嗎?

我認為更實用的方法是:

let intoMap (data: seq<MyData>) = 
    Seq.fold (fun (datamap, dups) (data: MyData) -> 
        match datamap |> Map.tryFind data.Id with
        | Some submap when submap |> Map.containsKey data.Analysis -> 
            datamap, data :: dups
        | Some submap ->
            let ext = Map.add data.Analysis data submap
            (Map.add data.Id ext datamap), dups
        | None ->
            let submap = Map.ofArray [| (data.Analysis, data) |]
            (Map.add data.Id submap datamap), dups
        ) (Map.empty, List.empty) data

它是數據的折疊,因此它遍歷序列一次。 它還具有更多的功能,因為它不會產生副作用-收集日志並將其作為輸出的一部分,而不是記錄重復項。 以后您可以對他們進行任何操作。

另外,我使用不變的Map而不是Dictionary-我發現Dictionary是F#代碼中的一種代碼味道。 它提供的可變性在更深奧的場景中有其用途,但是對於實際保存和傳遞數據,我將專門使用Map。

這是您眼前一個問題的答案-但老實說,我可能會選擇一個單獨的函數來查找和拆分重復項,並使用一個單獨的函數來構建地圖而無需照顧潛在的重復項-即使那意味着多個傳遞數據。

根據您的要求,您所擁有的可能是最好的。 您可以使用模式匹配來加緊代碼。

let dataDict = Dictionary<_,Dictionary<_,_>>()
for d in data do
    match dataDict.TryGetValue(d.Id) with
    | true, m when m.ContainsKey(d.Analysis) ->
        (d.Id, d.Analysis)
        ||> sprintf "Id '%s' has more than one analysis of type '%s', rows will be ignored" 
        |> logger.Warn
    | true, m -> 
        m.Add(d.Analysis, d)
    | _ ->
        let m = Dictionary()
        m.Add(d.Analysis, d)
        dataDict.Add(d.Id, m)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM