2011-01-13 68 views
7

Tôi đang tìm cách tạo chuỗi bao gồm mọi phần tử thứ n của một chuỗi khác, nhưng dường như không tìm cách làm điều đó một cách thanh lịch. Dĩ nhiên tôi có thể hack một thứ gì đó, nhưng tôi tự hỏi liệu có một chức năng thư viện mà tôi không nhìn thấy hay không.Bắt mọi phần tử thứ n của một chuỗi

Các hàm chuỗi có tên kết thúc bằng -i có vẻ khá tốt cho mục đích tìm ra khi phần tử là phần thứ n hoặc (bội số của n), nhưng tôi chỉ có thể thấy iterimapi, không có trong đó thực sự tự vay cho nhiệm vụ.

Ví dụ:

let someseq = [1;2;3;4;5;6] 
let partial = Seq.magicfunction 3 someseq 

Sau đó partial nên [3;6]. Có điều gì giống như nó ra khỏi đó?

Edit:

Nếu tôi không phải là khá tham vọng và cho phép n là không đổi/nổi tiếng, sau đó tôi vừa phát hiện ra rằng sau nên làm việc:

let rec thirds lst = 
    match lst with 
    | _::_::x::t -> x::thirds t // corrected after Tomas' comment 
    | _ -> [] 

Would có cách nào để viết ngắn hơn không?

+3

Bạn có thể sử dụng 'mapi' để biến từng phần tử của danh sách thành' Số' hoặc 'Không',' lọc' ra khỏi 'Không', và sau đó 'ánh xạ' chúng trở lại loại chưa được đặt trước. –

+0

Giải pháp của bạn sử dụng danh sách có vẻ tốt (nhưng bạn có thể muốn viết '_ :: _ :: x :: t' (thay vì' (_, _, x) :: t' sử dụng danh sách các bộ dữ liệu). là 'Seq' sẽ làm việc với các bộ sưu tập khác ngoài danh sách, nhưng điều đó có thể không phải là vấn đề đối với bạn. Phiên bản của bạn với danh sách là một mã chức năng tốt. –

+0

Có, tất nhiên, nó phải là' _ :: _ :: x :: t', nên đã yêu cầu trình biên dịch trước khi dán nó ở đây –

Trả lời

8

Bạn có thể nhận hành vi bằng cách sáng tác mapi với các chức năng khác:

let everyNth n seq = 
    seq |> Seq.mapi (fun i el -> el, i)    // Add index to element 
     |> Seq.filter (fun (el, i) -> i % n = n - 1) // Take every nth element 
     |> Seq.map fst        // Drop index from the result 

Giải pháp sử dụng tùy chọn và choose theo đề nghị của Annon sẽ sử dụng chỉ có hai chức năng, nhưng cơ thể của người đầu tiên sẽ hơi hơn phức tạp (nhưng nguyên tắc cơ bản là giống nhau).

Một phiên bản hiệu quả hơn bằng cách sử dụng đối tượng IEnumerator trực tiếp không phải là quá khó khăn để viết:

let everyNth n (input:seq<_>) = 
    seq { use en = input.GetEnumerator() 
     // Call MoveNext at most 'n' times (or return false earlier) 
     let rec nextN n = 
      if n = 0 then true 
      else en.MoveNext() && (nextN (n - 1)) 
     // While we can move n elements forward... 
     while nextN n do 
      // Retrun each nth element 
      yield en.Current } 

EDIT: Đoạn cũng có sẵn ở đây: http://fssnip.net/1R

+0

Không biết ai nhanh hơn, bạn hoặc 'Anon', nhưng nó đều là cùng một gợi ý, phải không? Nó trông không hiệu quả khủng khiếp, dù sao, phải không? –

+0

Nó không phải là khủng khiếp hiệu quả (có một số cuộc gọi chức năng bổ sung và indirections, bởi vì nó sử dụng 3 vòng lặp dưới bìa), nhưng nó có thể không quá xấu (không có danh sách trung gian sẽ phải được phân bổ). Đối với một phiên bản hiệu quả hơn, bạn sẽ cần đột biến (trong biểu thức trình tự) hoặc sử dụng phần tử bên dưới 'IEnumerator' –

9

Seq.choose công trình độc đáo trong những tình huống này bởi vì nó cho phép bạn thực hiện tác vụ filter trong lambda mapi.

let everyNth n elements = 
    elements 
    |> Seq.mapi (fun i e -> if i % n = n - 1 then Some(e) else None) 
    |> Seq.choose id 

Tương tự như here.

+0

Đẹp và thanh lịch! –

+0

Vâng, tôi cũng thích nó! :) –

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