سؤال

أواجه بعض المشاكل في إجراء تسلسل. أساسا أحتاج إلى تقطيع تسلسل في سلسلة من الصفائف. seq.windowed تقريبا يفعل ذلك ولكني لا أريد العناصر المكررة.

يمكنني الحصول على ما أريده عن طريق قراءة كل شيء في صفيف أولا ولكنني أفضل استخدام تسلسل.

let array_chunk s (a:int[]) =
    Array.init (a.Length / s) (fun i -> Array.sub a (i * s) s)

someSequence |> Seq.to_array |> array_chunk 5
هل كانت مفيدة؟

المحلول

إليك واحدة من الضرورة الجميلة التي سيعمل مع SEQ وتوليد صفائف من أي حجم. آخر واحد سيكون أصغر إذا كان التسلسل ليس حتى ن.

let chunk n xs = seq {
    let i = ref 0
    let arr = ref <| Array.create n (Unchecked.defaultof<'a>)
    for x in xs do
        if !i = n then 
            yield !arr
            arr := Array.create n (Unchecked.defaultof<'a>)
            i := 0 
        (!arr).[!i] <- x
        i := !i + 1
    if !i <> 0 then
        yield (!arr).[0..!i-1] }

نصائح أخرى

أنا أحب Seq.take & Seq.skip المحلول. إنه جميل وبسيط وقابل للقراءة للغاية، لكنني كنت أستخدم شيئا مثل هذا:

let chunks n (sequence: seq<_>) =
    let fold_fce (i, s) value = 
        if i < n then (i+1, Seq.append s (Seq.singleton value))
                 else (  1, Seq.singleton value)
    in sequence
    |> Seq.scan (fold_fce) (0, Seq.empty)
    |> Seq.filter (fun (i,_) -> i = n)
    |> Seq.map (Seq.to_array << snd )

ليس رمز حتمي وينبغي أن يكون أكثر كفاءة من الحل الذي يستخدم SEQ.Skip. من ناحية أخرى، فإنها تسلسل الإدخال إلى الطول قابل للقسمة من قبل ن. إذا كان هذا السلوك غير مقبول، فيمكن إصلاحه بواسطة تعديل بسيط:

let chunks n (sequence: seq<_>) =
    let fold_fce (i, s) value = 
        if i < n then (i+1, Seq.append s (Seq.singleton value))
                 else (  1, Seq.singleton value)
    in sequence
    |> Seq.map (Some)
    |> fun s -> Seq.init_finite (n-1) (fun _ -> None) |> Seq.append s
    |> Seq.scan (fold_fce) (0, Seq.empty)
    |> Seq.filter (fun (i,_) -> i = n) 
    |> Seq.map (Seq.to_array << (Seq.choose (id)) << snd )

من المحتمل أن تدفن هذه الإجابة، لكن إليك تأخذ المشكلة:

let chunk n xs = 
    xs 
    |> Seq.mapi(fun i x -> i/n, x)
    |> Seq.groupBy fst
    |> Seq.map (fun (_, g) -> Seq.map snd g)

الايجابيات:

  • يستخدم SEQ فقط، لا توجد صفائف
  • o (n) وقت التشغيل. لا o (n ^ 2) مثل seq.skip / ake الحلول
  • seq.length لا يجب أن يكون مضاعف n
  • صغيرة وسهلة الفهم؟

سلبيات:

  • ربما لا تكون فعالة مثل حلقات حتمية / قابلة للتغيير

ماذا عن:

let rec chunks n sq =
  if not (Seq.is_empty sq) then 
    seq {
      yield Seq.take n sq |> Seq.to_array
      yield! chunks n (Seq.skip n sq)
    }
  else
    Seq.empty

لاحظ أن هذا يتطلب SQ أن يكون لديك عدد من العناصر التي يتم تقسيمها بالتساوي بواسطة N (لأن SEQ.Take و Seq.Skip، على عكس أساليب LinQ's Take وتخطي طرق التمديد، تتطلب أن يحتوي التسلسل على عناصر N على الأقل). أيضا، هذا ليس فعالا مثل استخدام العداد بشكل صريح، لكنه أكثر أناقة.

نسخة تصحيح من إجابة التقط / تخطي، كدالة تمديد. يجب أن تعمل لأطوال غير متساوية. لا ضمانات للأداء على الرغم من ...

 module Seq = 
    let rec chunks n (s:#seq<_>) =
        seq {
                 if Seq.length s <= n then
                    yield s
                 else
                    yield Seq.take n s
                    yield! chunks n (Seq.skip n s)           
            }

(الرمز مأخوذ من إجابتي هنا)

هذا لطيف ومخلص:

let chunk size (arr : 'a array) =
    [| for a in 0 .. size .. arr.Length - size -> arr.[a..a + size - 1] |]

ومع ذلك، فإن هذه إخلاق عناصر (Arr.Length٪ الحجم) في الصفيف. يمكنك إصلاح هذا عن طريق الاستيلاء على العناصر المفقودة واستخدام Array.Append:

let chunk2 size (arr : 'a array) = 
    let first = [| for a in 0 .. size .. arr.Length - size -> arr.[a..a + size - 1] |]
    let numberOfMissingElements = arr.Length - (first.Length * size)
    if numberOfMissingElements > 0 then
        let last = [| arr.[arr.Length - numberOfMissingElements..] |]
        Array.append first last
    else first

فيما يلي نهج آخر مع بعض مطابقة النمط - يبدو أكثر مثل *. القوائم، ولدي البصق قوائم بدلا من صفائف، لأن هذا هو الطريقة التي عادة ما أحب بياناتي.

let sequence_in_lists_of_length_n_with_acc (s: seq<'a>) n acc = seq {
use e = s.GetEnumerator()
let rec next_with_acc acc =
  match e.MoveNext(), acc with
  | true, a when List.length a + 1 = n ->  
    yield (List.rev (e.Current :: a))
    next_with_acc []
  | true, _ -> next_with_acc (e.Current :: acc)
  | false, _ ->
    f(List.rev acc)
  ()
next_with_acc []

}

أنا أحب هذا الحل أفضل. إنه يولد تسلسل جديد من التسلسل الموجود (مما يعني أنه لا يحتاج إلى اجتياز التسلسل بأكمله للحصول على نتيجة - هذا أمر بالغ الأهمية إذا كنت تفعل شيئا مثل معالجة السجلات، حيث لا يمكنك استدعاء أشياء مثل الطول).

انتهى بي الأمر كتابة مشاركة مدونة مع مزيد من التفاصيل حول كيف وصلت إلى هنا.

module Seq =  

دع grouped_by_with_leftover_processing f (f2: 'قائمة -> قائمة <' 'a>) (S: SEQ <a>) = دع REC Grouped_by_with_with_Aff (F: "A -" قائمة -> "خيار القائمة *" القائمة) ACC (أي: ienumerator <a>) = SEQ {IF IE.MOVENEXT () ثم ترك NextValue، بقايا الطعام = F IE.Current ACC إذا NextValue.issome ثم تستجع NextValue.Value! بقايا بقايا Grouped_by_with_Acc f أي آخر، دع Rems = f2 acc إذا rems.issome ثم تسفر عن rems.value} SEQ {Ground! grouped_by_with_acc f [] (s.getenumerator ())}

اسمحوا RecipreversedLefers (F: "قائمة) = إذا f.isempty ثم لا شيء آخر بعض (list.rev F)

دع grouped_by fs = grouped_by_with_leftover_processing f roeldersedlefterss s

دع group_by_length_n ns = دع grouping_function newvalue acc = اسمح NewList = newvalue :: acc // إذا كان لدينا الطول الصحيح، والعودة // a بعض القيمة الأولى. التي سوف تسفر عن التسلسل. إذا List.Length ACC = N - 1 ثم بعض (List.Rev NewList)، [] // إذا لم يكن لدينا الطول الصحيح، // لا يستخدم أي شيء (لذلك لن يتم تحقيق أي شيء)
grouped_by grouping_functions s.

تسلسل كبير ليست مشكلة:

seq {for i in 1..1000000000 -> i} |> seq.group_by_liver_n 3 ؛؛ فال ذلك: SEQu003Cint list> = SEQ [[1؛ 2؛ 3]؛ [4؛ 5 6]؛ [7؛ 8؛ 9]؛ [10؛ 11؛ 12]؛ ...] >>

تم إصلاح نسخة لطيفة من الأميرة للحصول على الذيل وتحويلها إلى SEQ

let array_chunk size (arr : 'a array) = 
    let maxl = arr.Length - 1
    seq { for a in 0 .. size .. maxl -> arr.[a .. min (a + size - 1) maxl ] }

ماذا عن هذه :

let grouped n = 
   Seq.unfold(fun s -> if not (Seq.isEmpty s) then 
                           Some (Seq.take n s, Seq.skip n s) 
                       else None) 

إنه في نفس الوريد من إجابة KVB.

أتذكر بطريقة أو بأخرى (رابط؟) أن التسلسل لا يتذكر الموضع، بحيث لن يكون التقط / تخطي التتابع غير مثالي.

هنا حل @ KVB مع seq.skip / التقاط القيود الثابتة. انها صغيرة وأنيقة، و (ن).

let eSkip n s = System.Linq.Enumerable.Skip(s, n)

let rec seq_chunks n sq =
  if (Seq.isEmpty sq) 
  then Seq.empty
  else seq {
      yield Seq.truncate n sq
      yield! seq_chunks n (eSkip n sq)
 }

إليك نسختي تأخذ مجموعة كمدخلات وإخراج:

  let chunk chunkNumber (array : _ array) =
    let chunkSize = array.Length/chunkNumber
    let mutable startIndex = 0
    [|
        let n1 = array.Length % chunkNumber
        for i = 1 to n1 do
            yield Array.sub array startIndex (chunkSize+1)
            startIndex <- startIndex + chunkSize+1

        let n2 = chunkNumber - n1
        for i = 1 to n2 do
            yield Array.sub array startIndex chunkSize
            startIndex <- startIndex + chunkSize
    |]

تحاول الوظيفة إجراء قطع من حجم مماثل (بدلا من الحصول على قطعة صغيرة صغيرة جدا) ويبني الناتج بنفس الطريقة التي ستقوم ببناء تسلسل (مما يجعل من السهل إعادة كتابةها من أجل الحصول على تسلسل كإخراج)

تلخيص الجزء العلوي أو التخزين المؤقت أو التخزين المؤقت ل Seqence أو قائمة أو صفيف. شكلين:

let rec chunk size xs = seq {
    yield Seq.take size xs
    yield! chunk size (Seq.skip size xs)
    }

أو

let chunk size = Seq.unfold (fun xs ->
    match Seq.isEmpty xs with
    | false -> Some(Seq.take size xs, Seq.skip size xs)
    | _ -> None
    )

ملاحظة: إذا كان SEQ يعمل بشكل صحيح مثل المؤشر (كما كنت أتوقع أن يكون التقييم كسول)، فإن SEQ.take ستقدم الموضع SEQ.Skip لن يكون ضروريا. ومع ذلك، هذا ليس هو الحال.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top