2015-12-28 11 views
5

मैं इस तरह डेटा पैक करने के लिए की जरूरत है की एक सूची/सरणी का निर्माण:एफ # मूल्यों + लगातार डुप्लिकेट

let data = [1; 2; 2; 3; 2; 2; 2; 4] 
let packed = [(1, 1); (2, 2); (3, 1); (2, 3); (4, 1)] 

कहाँ प्रत्येक आइटम का कहना है कि कितना बार यह अगले पहले से मौजूद हैं। हालांकि, यह गैर-आसन्न डुप्लिकेशंस के साथ काम करना चाहिए।

मैं इसे शास्त्रीय अनिवार्य कोड के साथ काम कर सकता हूं, लेकिन आश्चर्य कीजिए कि यह कार्यात्मक रूप से कैसे कार्य करता है।

इसके अलावा, Seq.countBy काम नहीं है, क्योंकि यह खाते में लेने के सभी मूल्यों

उत्तर

6

, आप कर सकते हैं follow a set of small steps to refector to a recursive implementation:

let groupFunc list = 
    let rec groupFuncRec acc lst init count = 
     match lst with 
     | [] -> List.rev acc 
     | head::[] when head = init 
      -> groupFuncRec ((init, count)::acc) [] 0 0 
     | head::[] when head <> init 
      -> groupFuncRec ((head, 1)::acc) [] 0 0 
     | head::tail when head = init 
      -> groupFuncRec acc tail head (count+1) 
     | head::tail when head <> init 
      -> groupFuncRec ((init, count)::acc) tail head 1 
    let t = List.tail list 
    let h = List.head list 
    groupFuncRec [] t h 1 

जब मैं अपने नमूना डेटा पर समारोह को चलाने मैं वापस अपेक्षित परिणाम मिलता है।

Recursion

जब मैं अपने जरूरी संस्करण की तरह, यहाँ एक पुनरावर्ती संस्करण है लग रहा है क्या पता नहीं है:

let pack xs = 
    let rec imp acc = function 
    | [] -> acc 
    | h::t -> 
     match acc with 
     | [] -> imp [(h, 1)] t 
     | (i, count) :: ta -> 
      if h = i 
      then imp ((i, count + 1) :: ta) t 
      else imp ((h, 1) :: (i, count) :: ta) t 
    xs |> imp [] |> List.rev 

इस समारोह प्रकार 'a list -> ('a * int) list when 'a : equality है। यह काम करने के लिए imp नामक एक निजी 'कार्यान्वयन समारोह' का उपयोग करता है। यह फ़ंक्शन रिकर्सिव है, और थ्रेड एक संचयक (जिसे acc कहा जाता है) पूरे होते हैं। यह संचयक परिणाम सूची है, जिसमें ('a * int) list है।

तो संचायक सूची खाली है, मूल सूची (h) के प्रमुख के रूप में भी गिनती 1, अद्यतन संचायक का एकमात्र ऐसा तत्व के रूप में एक टपल के रूप में बनाया गया है, और imp समारोह रिकर्सिवली साथ कहा जाता है वह अद्यतन संचयक।

यदि संचयक में पहले से ही कम से कम एक तत्व होता है, तो तत्व पैटर्न मिलान के माध्यम से निकाला जाता है, और उस ट्यूपल (i) में तत्व h से तुलना किया जाता है। यदि h = i, संचयक अद्यतन किया गया है; अन्यथा, acc पर एक नया ट्यूपल लगाया जाता है। दोनों मामलों में, हालांकि, imp को नए संचयक के साथ दोबारा बुलाया जाता है।

आप एक सूची इस तरह अपने मूल टपल के बराबर से कॉल करने की कर सकते हैं:

> pack [1; 2; 2; 3; 2; 2; 2; 4];; 
val it : (int * int) list = [(1, 1); (2, 2); (3, 1); (2, 3); (4, 1)] 

फोल्ड

एक बार जब आप एक पुनरावर्ती संस्करण है, तो आप अक्सर एक संस्करण के लिए नुस्खा एक का उपयोग कर तह। इस मामले में, उपर्युक्त pack फ़ंक्शन को अंत में संचयक को पीछे हटाना है (List.rev का उपयोग करके), दाएं गुना सबसे उपयुक्त है।एफ # में, इस के साथ निर्मित List.foldBack समारोह किया जाता है:

let pack' xs = 
    let imp x = function 
     | (i, count) :: ta when i = x -> (i, count + 1) :: ta 
     | ta -> (x, 1) :: ta 
    List.foldBack imp xs [] 

इस मामले में, समारोह List.foldBack के लिए पारित एक सा भी एक गुमनाम समारोह के रूप में पारित करने के लिए जटिल है, इसलिए मैं एक के रूप में यह परिभाषित करने के लिए चुना है निजी आंतरिक समारोह। यह उपरोक्त pack फ़ंक्शन द्वारा उपयोग किए गए रिकर्सिव imp फ़ंक्शन के समतुल्य है, लेकिन आप यह स्वीकार करेंगे कि इसे स्वयं को रिकर्सिव रूप से कॉल करने की आवश्यकता नहीं है। इसके बजाय, इसे केवल संचयक के लिए नया मूल्य वापस करना होगा।

परिणाम एक ही है:

> pack' [1; 2; 2; 3; 2; 2; 2; 4];; 
val it : (int * int) list = [(1, 1); (2, 2); (3, 1); (2, 3); (4, 1)] 
+2

अच्छा जवाब। मेरी राय में कुछ चीजें छोटे बदलाव इसे और भी स्पष्ट कर सकती हैं। आप पहचानने वाले नामक से बचने के लिए 'let imp x acc = match acc ...' के साथ 'let imp x acc = match acc ...' को प्रतिस्थापित कर सकते हैं जिसे आप तुरंत तुरंत नष्ट कर देते हैं। सबसे महत्वपूर्ण बात यह है कि आप पैटर्न मिलान के मामलों के क्रम को स्विच कर सकते हैं और '[]' और 'x <> i' मामलों को एकीकृत करने के लिए 'कब' खंड का उपयोग कर सकते हैं:' | (मैं, गिनती) :: टा जब मैं = एक्स -> (i, गिनती + 1) :: टा टा -> (एक्स, 1) :: टा'। – kvb

+0

@kvb वे बहुत अच्छे सरलीकरण हैं! धन्यवाद। उत्तर अपडेट किया गया। –

1

मेरे समाधान मान लिया गया data संग्रह एक सूची है। यदि इसे टुपल के रूप में रखना (आपके उदाहरण के अनुसार) जानबूझकर था तो काम करने के मेरे समाधान के लिए टुपल को एक सूची में परिवर्तित करना होगा (उदाहरण के लिए यह कैसे करें here पाया जा सकता है)। आप पहले से ही एक अनिवार्य संस्करण है, तो

list = [(1, 1); (2, 2); (3, 1); (4, 1)] 
1

आप अपने तर्क में कुछ स्थितीय जानकारी शामिल करके काम करने के लिए Seq.countBy मिल सकती है। बेशक, आपको अपने मूल डेटा पर वापस मैप करने की आवश्यकता है।

[1; 2; 2; 3; 2; 2; 2; 4] 
|> Seq.scan (fun (s, i) x -> 
    match s with 
    | Some p when p = x -> Some x, i 
    | _ -> Some x, i + 1) (None, 0) 
|> Seq.countBy id 
|> Seq.choose (function 
| (Some t, _), n -> Some(t, n) 
| _ -> None) 
|> Seq.toList 
// val it : (int * int) list = [(1, 1); (2, 2); (3, 1); (2, 3); (4, 1)] 
संबंधित मुद्दे