2011-07-17 10 views
7

एक समूह आईडी/मूल्य tuples के एक दृश्य को देखते हुए यह समूह योग की गणना करने के लिए आसान (काफी उसी तरह मैं के साथ सी # और LINQ यह करना होगा) था:एफ # में ग्रुप योग - अनुक्रमों के साथ आसान, क्या यह सूचियों के साथ संभव है?

let items = ["g1",5; "g2",10; "g1",20] 

let groupsums = 
    items 
    |> Seq.groupBy (fun x -> fst x) 
    |> Seq.map (fun (g, s) -> Seq.fold (fun acc x -> acc + snd x) 0 s) 

लेकिन एफ # करने के लिए नया किया जा रहा है, मैं सूचियों के साथ ऐसा करने का कोई तरीका नहीं देख सकता है। क्या मुझे म्यूटेबल वैरिएबल का उपयोग करना है, या सूचियों के साथ ऐसा करने का एक कार्यात्मक तरीका है?

उत्तर

8

List.groupBy में कोई निर्मित नहीं है। प्रकारों में निर्मित कई एफ # में फ़ंक्शन हैं जिन्हें कहा गया फ़ंक्शन का सीक संस्करण असाइन किया गया है। जैसे list.fs

let inline sumBy f (list : list<_>) = Seq.sumBy f list

से मैं बहुत यकीन है कि एफ # के डिजाइनर क्या स्थिरता की खातिर और क्या सूखी की खातिर के लिए छोड़ के लिए नकल करने के बारे में कई विचार विमर्श किया है। मैं व्यक्तिगत रूप से चाहता हूं कि वे DRY के साथ फंस जाएं।

यदि आप अपना खुद का "कार्यात्मक" बनाना चाहते हैं List.groupBy मैं मानचित्र और सूची का उपयोग करूंगा।

let groupBy list = 
    list 
    |> List.fold (fun group (g, x) -> 
     match group |> Map.tryFind g with 
     | Some(s) -> group |> Map.remove g |> Map.add g (x::s) 
     | None -> group |> Map.add g [x] 
     ) Map.empty 
    |> Map.toList 

let groupsums = groupBy >> List.map (snd >> List.sum) 

यदि आपको केवल योग की आवश्यकता है तो आप रखरखाव सूचियों को छोड़ सकते हैं।

let groupAndSumBy list = 
    list 
    |> List.fold (fun group (g, x) -> 
     match group |> Map.tryFind g with 
     | Some(s) -> group |> Map.remove g |> Map.add g (x + s) 
     | None -> group |> Map.add g x 
     ) Map.empty 
    |> Map.toList 
    |> List.map snd 

आउटपुट

> groupsums items;; 
val it : int list = [25; 10] 

> groupAndSumBy items;; 
val it : int list = [25; 10] 
+0

मैंने देखा नहीं .groupBy है;) लेकिन शायद वहाँ एफ # "जादू" किसी तरह का करने के लिए एक रास्ता है और ग्रुपिंग से बचें? इसके अलावा, "अपना खुद का निर्माण करने के लिए। समूह में मैं मानचित्र और सूची का उपयोग करूंगा" - सूची क्या है? सूची में .map है लेकिन कोई सूची नहीं है। –

+0

@ सर्गी मैंने एक संभावित समाधान जोड़ा। – gradbot

+0

धन्यवाद। क्या आपको लगता है कि प्रदर्शन या स्मृति पदचिह्न में कोई उल्लेखनीय अंतर होगा? –

7

gradbot समाधान के साथ कुछ भी गलत नहीं है, वहीं मैं सिर्फ यह सरल रखने के लिए और Seq.toList का उपयोग करते वांछित सूची में वापस दृश्यों कन्वर्ट करने के लिए चाहते हैं। तो आप के रूप में अपनी परिभाषा को फिर से लिखने सकता है:

let groupsums = 
    items 
    |> Seq.groupBy fst 
    |> Seq.toList 
    |> List.map (fun (_,s) -> Seq.sumBy snd s) 
+0

कम नोटेशन के लिए धन्यवाद, लेकिन सूची यहां कृत्रिम है। –

+1

@ सर्गी - जबकि यह सच है, ग्रुपिंग करते समय सूची की संरचना का लाभ उठाने का कोई वास्तविक तरीका नहीं है, वैसे भी, इसलिए आप अपनी खुद की 'List.groupBy लिखने के बजाय' Seq.groupBy' का उपयोग करके वास्तव में बहुत कुछ नहीं खोते हैं। '(आपको किसी भी मामले में एक मध्यवर्ती मानचित्र संरचना का उपयोग करना होगा)। – kvb

+0

जब तक आपको केवल समूह फोल्डिंग (@gradbot उत्तर का दूसरा भाग) की आवश्यकता नहीं होती है, इस स्थिति में आपको समूहों को स्मृति में रखने की आवश्यकता नहीं है। मुझे लगता है कि सबसे अच्छा प्रदर्शन/मेमोरी समाधान एक मानचित्र में अनुक्रम को फोल्ड करेगा। –

7

हालांकि मैं, KVB के सुझाव का प्रयोग करेंगे आप अपने खुद के रोल करने जा रहे हैं, मैं Dictionary बजाय Map उपयोग करने का सुझाव। मेरे परीक्षण में यह कम से कम 400% तेज था।

let groupBy f (list:list<_>) = 
    let dict = Dictionary() 
    for v in list do 
    let k = f v 
    match dict.TryGetValue(k) with 
    | true, l -> dict.[k] <- v :: l 
    | _ -> dict.Add(k, [v]) 
    dict |> Seq.map (|KeyValue|) |> Seq.toList 

या:

let groupSumBy (list:list<_>) = 
    let dict = Dictionary() 
    for k, v in list do 
    match dict.TryGetValue(k) with 
    | true, n -> dict.[k] <- v + n 
    | _ -> dict.Add(k, v) 
    dict |> Seq.map (|KeyValue|) |> Seq.toList 

रेफरी संस्करण द्वारा:

let groupSumBy (list:list<_>) = 
    let dict = Dictionary() 
    let mutable n = 0 
    for k, v in list do 
    match dict.TryGetValue(k, &n) with 
    | true -> dict.[k] <- v + n 
    | false -> dict.Add(k, v) 
    dict |> Seq.map (|KeyValue|) |> Seq.toList 
+1

+1, और आप TryGetValue के "रेफरी" संस्करण का उपयोग करके उस पर शीर्ष 15% की वृद्धि कर सकते हैं (मैंने देखा कि जोन हैरोप ने कहीं उल्लेख किया है कि ट्यूपल पैटर्न मिलान संस्करण कुछ अतिरिक्त ढेर आवंटन करता है जो मैं करने में सक्षम था यहां 'groupBy' का परीक्षण करें) देखें। –

+0

वाह, यह प्रश्न मेरे जैसे एफ # नोब के लिए बहुत उपयोगी हो गया है;) धन्यवाद दोस्तों! –

+0

@ स्टीफन: मुझे नहीं पता था। जानना बहुत अच्छा है! – Daniel

संबंधित मुद्दे