2009-04-04 29 views
6

Tôi gặp sự cố khi tạo chuỗi. Về cơ bản tôi cần phải cắt một chuỗi thành một chuỗi các mảng. Seq.windowed gần như không nhưng tôi không muốn các yếu tố trùng lặp.F # array_chunk cho Trình tự

Tôi có thể nhận được những gì tôi muốn bằng cách đọc tất cả mọi thứ vào một mảng đầu tiên nhưng tôi muốn sử dụng một chuỗi.

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 

Trả lời

5

Đây là một mệnh lệnh tốt đẹp sẽ hoạt động với seq và tạo các mảng có kích thước bất kỳ. Cái cuối cùng sẽ nhỏ hơn nếu dãy không bằng n.

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] } 
+0

Câu trả lời hay. Tôi đã gần với điều này với mã của tôi nhưng không hoàn toàn có nó. – gradbot

3

Làm thế nào về:

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 

Lưu ý rằng điều này đòi hỏi vuông có một số yếu tố đó là chia hết cho n (vì Seq.take và Seq.skip, không giống như Thi và Bỏ qua phần mở rộng LINQ của phương pháp, yêu cầu trình tự chứa ít nhất n phần tử). Ngoài ra, điều này là không hiệu quả như một cách rõ ràng bằng cách sử dụng điều tra sẽ được, nhưng nó thanh lịch hơn.

+0

Tôi muốn upvote cho đệ quy seq (một kỹ thuật cá nhân tôi sử dụng rất nhiều), nhưng mã này ném một ngoại lệ khi sq.Length không chia hết cho n. – Juliet

+0

Vâng, tôi đã đề cập rằng sau mã. Sẽ tốt hơn nếu Seq.take và Seq.skip được triển khai giống như các hoạt động tương ứng của LINQ, vì vậy chúng không yêu cầu ít nhất là nhiều phần tử đang được thực hiện hoặc bỏ qua. – kvb

+0

Xem câu trả lời của tôi here Benjol

5

Tôi yêu giải pháp Seq.take & Seq.skip. Nó là đẹp, đơn giản và rất dễ đọc, nhưng tôi sẽ sử dụng một cái gì đó như thế này:

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) 

Nó không phải là mã bắt buộc và nó sẽ có hiệu quả hơn so với các giải pháp sử dụng Seq.skip. Mặt khác, nó cắt chuỗi đầu vào thành độ dài chia hết cho n. Nếu hành vi này là không thể chấp nhận nó có thể được cố định bằng sự thay đổi đơn giản:

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) 
+0

Tôi đã đi với câu trả lời này là học tập để hiểu mã này đã cho tôi cái nhìn sâu sắc hơn vào F #. – gradbot

+0

Tôi đã làm một băng ghế dự bị nhanh và giải pháp đầu tiên của bạn chậm hơn khoảng 50% so với giải pháp cấp bách của MichaelGG và giải pháp thứ hai chậm hơn khoảng 100%. Tôi đã sử dụng giải pháp đầu tiên của bạn. Cảm ơn :) – gradbot

+1

Để sử dụng kiểu vô nghĩa, bạn có thể thực hiện "|> Seq.filter (fst >> (=) n)" "|> Seq.filter (vui vẻ (i, _) -> i = n)". – MichaelGG

1

này là tốt đẹp và gọn gàng:

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

Tuy nhiên, đây lops ra khỏi cuối cùng (arr.Length kích thước%) các yếu tố trong mảng. Bạn có thể khắc phục điều này bằng cách lấy các yếu tố mất tích và sử dụng 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 
0

Dưới đây là cách tiếp cận khác với một số mô hình kết hợp - nó trông giống như * .iter, và tôi đã bị nó phun ra danh sách chứ không phải là mảng vì đó là cách tôi thường thích dữ liệu của mình.

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 [] 

}

0

Tôi thích giải pháp này tốt hơn. Nó tạo ra một chuỗi mới từ chuỗi hiện tại (có nghĩa là nó không cần phải đi qua toàn bộ chuỗi để có được kết quả - điều đó quan trọng nếu bạn đang làm một cái gì đó như xử lý nhật ký, nơi bạn không thể gọi những thứ như Độ dài).

Tôi đã kết thúc bằng văn bản blog post với thông tin chi tiết hơn về cách tôi nhận tại đây.

module Seq = 

hãy grouped_by_with_leftover_processing f (f2: 'một danh sách -> danh sách <' a> tùy chọn) (s: seq < 'a>) = hãy grouped_by_with_acc rec (f:' a -> 'một danh sách - > 'một danh sách tùy chọn *' danh sách) acc (ví dụ: IEnumerator < 'a>) = seq { nếu ie.MoveNext() sau đó hãy nextValue, thức ăn thừa = f ie.Current acc nếu nextValue.IsSome sau đó yield nextValue.Giá trị lợi nhuận! thức ăn thừa grouped_by_with_acc f tức là khác hãy REMS = f2 acc nếu rems.IsSome sau đó mang lại rems.Value } seq { năng suất! grouped_by_with_acc f [] (s.GetEnumerator()) }

hãy YieldReversedLeftovers (f: 'một danh sách) = nếu f.IsEmpty sau đó Không khác Một số (List.rev f)

let grouped_by fs = grouped_by_with_leftover_processing f YieldReversedLeftovers s

hãy group_by_length_n ns = hãy grouping_function newValue acc = hãy để newList = newValue :: acc // Nếu chúng ta có chiều dài đúng, trở về // a Một số là giá trị đầu tiên. Điều đó sẽ // được tạo bởi chuỗi. nếu List.length acc = n - 1 thì một số (List.rev newList), [] // Nếu chúng ta không có độ dài phù hợp, // use None (do đó không có gì sẽ được tạo) else None , newList
grouped_by grouping_function s

chuỗi lớn không phải là một vấn đề:

seq {for i in 1..1000000000 - > i} | > Seq.group_by_length_n 3 ;; val it: seq < int danh sách > = seq [[1; 2; 3]; [4; 5; 6]; [7; số 8; 9]; [10; 11; 12]; ...] >
0

đẹp phiên bản từ công chúa đã được cố định để có được đuôi và chuyển đổi thành 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 ] } 
3

phiên bản Corrected cất/skip câu trả lời, như chức năng mở rộng. Nên làm việc cho độ dài không đồng đều. Không đảm bảo cho hiệu suất mặc dù ...

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)   
      } 

(Mã lấy từ câu trả lời của tôi here)

+0

Trong khi điều này đơn giản và sạch sẽ đó là O (n^2). Cảm ơn vì lời đáp sâu sắc. :) – gradbot

+1

Tôi không có chuyên gia, nhưng từ những gì tôi đã nhìn thấy, tối ưu hóa cho hiệu suất trong F # thường kết thúc với tính đột biến, không biết nó luôn luôn là trường hợp mặc dù. – Benjol

+1

Đó là O (n^2) vì Seq.length là O (n) - nói chung. Nếu s là một mảng, thì Seq.length là O (1) và khối() chỉ là O (n) – Ray

0

Làm thế nào về vấn đề này một:

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

Chính trong bối cảnh đó hơn câu trả lời kvb của.

Tôi bằng cách nào đó nhớ (liên kết?) Rằng một chuỗi không nhớ vị trí, do đó việc tiếp tục/bỏ qua sẽ không được tối ưu.

4

Câu trả lời này có thể sẽ được chôn cất, nhưng đây là quan điểm của tôi về vấn đề này:

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

Ưu điểm:

  • Sử dụng chỉ seq, không có mảng
  • O (n) thời gian chạy. Không phải O (n^2) như Seq.skip/giải pháp
  • Seq.độ dài không phải là bội số của n
  • nhỏ và dễ hiểu?

Nhược điểm:

  • lẽ không hiệu quả như bắt buộc/vòng mutable
+0

Như một số hàm bên trong 'Seq' liệt kê toàn bộ tập hợp chúng được đưa ra ngay sau khi bạn truy cập phần tử đầu tiên của chúng . 'Seq.groupBy' sử dụng từ điển để thực hiện nhóm. https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/seq.fs#L1273 – gradbot

0

Dưới đây là @ kvb của giải pháp với Seq.skip/mất những hạn chế cố định. Nó nhỏ, thanh lịch và O (n).

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) 
} 
+0

Đây không phải là O (n). Đó là O (n^2). Hãy thử đi qua một chuỗi để in ra các đánh giá. 'seq {cho i trong 1 .. 15 làm printf"% A "i; sản lượng i} ' – gradbot

+0

Holy crap! bạn đúng. eSkip & Seq.skip đang làm những điều rất lạ ... – Ray

0

Dưới đây là phiên bản của tôi tham gia một mảng như là đầu vào và đầu ra:

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 
    |] 

Chức năng cố gắng để làm cho khối kích thước tương tự (thay vì nhận được một đoạn cuối cùng rất nhỏ) và xây dựng các sản lượng giống như cách bạn sẽ xây dựng một chuỗi (làm cho nó dễ dàng viết lại nó để có được một chuỗi như đầu ra)

Các vấn đề liên quan