2010-08-17 36 views
15

Tôi hiện đang học F # và tôi thực sự yêu thích toán tử yield! (yield-bang). Không chỉ cho tên của nó mà còn cho những gì nó làm tất nhiên.Sản lượng F #! nhà điều hành - Thực hiện và có thể C# tương đương

Toán tử yield! về cơ bản cho phép bạn mang lại tất cả các phần tử của chuỗi từ một biểu thức trình tự. Điều này rất hữu ích cho việc lập các điều tra viên. Vì tôi thường xuyên gặp những người điều tra lớn, phức tạp, tôi quan tâm đến các chiến lược mà chúng ta có thể sử dụng để chia nhỏ chúng và soạn chúng từ những điều tra đơn giản hơn.

Không may, nhà cung cấp yield! không khả dụng trong C#. Theo như tôi hiểu, những gì nó giống như một foreach (var x in source) yield x; nhưng cuốn sách tôi đang đọc (Petricek's Real World F# - Manning) cho thấy rằng nó có hiệu suất tốt hơn ...

  • Vì vậy, những gì chính xác nào để F # biên dịch làm ở đây? (Có, tôi có thể nhìn vào nó bằng cách sử dụng Reflector quá nhưng tôi muốn có một mô tả chi tiết hơn về cơ chế).

Để đạt được cấu trúc tương tự trong C# Tôi đã khám phá nhiều cách, nhưng không có cách nào ngắn gọn như toán tử yield! và tôi cũng không chắc chắn về độ phức tạp của chúng. Ai đó có thể vui lòng cung cấp đầu vào nếu số BigO của tôi là chính xác không?

  • Decompose Enumerator thành nhiều điều tra viên tư nhân và sau đó mang lại mỗi phần tử từ các điều tra viên công cộng:

    foreach (var x in part1()) yield x 
    foreach (var x in part2()) yield x 
    

    này một cách hiệu quả sẽ dẫn đến một "năng suất tăng gấp đôi" trên mỗi phần tử. Vậy đó là O (2n)? (hoặc có thể tồi tệ hơn?) Dù sao, bằng cách sử dụng cách tiếp cận này ngăn tôi sử dụng yield break; từ bất kỳ phân mục nào của tôi.

  • Decompose Enumerator thành nhiều điều tra viên tư nhân và sau đó concat tất cả các điều tra viên tư nhân từ các điều tra viên công cộng:

    return part1().Concat(part2()) 
    

    Tôi tin rằng đây là không có khác biệt so với các giải pháp nói trên vì Concat() được thực hiện theo cách tôi đã nêu trên.

Bất kỳ tùy chọn nào khác?

Trả lời

6

Về cách trình dịch dịch hoạt động yield!, the paper được trích dẫn bởi Thomas Levesque trong câu trả lời của ông minh họa một kỹ thuật thực hiện trong phần 4.3 (cụ thể, ví dụ về số liệu mở rộng 7-9 là minh họa cho chiến lược chung). Tôi không nghĩ rằng có cách nào tốt để làm điều này từ bên trong một khối lặp trong C# - khi tôi hiểu các giải pháp được đề xuất của bạn, cả hai có thể dẫn đến hành vi bậc hai khi được sử dụng đệ quy. Bạn luôn có thể tự tạo một lớp con NestedEnumerable<T> để đạt được các lợi ích hiệu suất, nhưng điều này sẽ khá xấu so với việc sử dụng một khối lặp bình thường.

7

Trong phiên bản hiện tại của C#, tôi không nghĩ rằng bạn có các tùy chọn khác hơn foreach... yield returnConcat. Tôi đồng ý sẽ tốt hơn nếu có một toán tử yield! trong C#, nó sẽ làm cho các cấu trúc nào đó thanh lịch hơn, nhưng tôi nghi ngờ tính năng này sẽ làm cho nó trở thành danh sách "phải có", vì chúng ta có thể dễ dàng thực hiện mà không có nó.

Bạn có thể quan tâm MS research paper này, trong đó giới thiệu một yield foreach xây dựng mới:

IEnumerable<XmlNode> Traverse(XmlNode n) 
{ 
    yield return n; 
    foreach (XmlNode c in n.ChildNodes) 
     yield foreach Traverse(c); 
} 

Về câu hỏi của bạn về độ phức tạp: trong cả hai trường hợp đó là O (n). O (2n) không được sử dụng, vì nó biểu thị độ phức tạp tương tự như O (n) (tuyến tính). Tôi không nghĩ rằng bạn có thể làm tốt hơn thế với các tính năng C# hiện tại ...

+1

Như giấy bạn trích dẫn chỉ ra, khi được sử dụng đệ quy có trường hợp mà 'yield!' (Tương đương với giả thuyết 'yield foreach' trong C#) là O (n) nhưng 'foreach ... yield return. ..' là O (n^2). – kvb

+0

@kvb: Phải, đó là người tôi đang nói đến, xin lỗi nếu điều này không rõ ràng. –

3

Không có đối tác trực tiếp nào với yield! trong C#. Bạn hiện đang bị mắc kẹt với sự kết hợp của foreachyield return.

Tuy nhiên, IIRC, LINQ cung cấp một cái gì đó tương tự, cụ thể là toán tử truy vấn SelectMany, chuyển thành C# là nhiều mệnh đề from .. in ...

(Tôi hy vọng tôi sẽ không trộn lên hai khái niệm khác nhau, nhưng IIRC, cả yield!SelectMany được về cơ bản "làm phẳng" dự báo;. Tức là một hệ thống các đối tượng được "san phẳng" vào một danh sách.)

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