2012-02-22 24 views
7

Cho số khởi đầu, hãy tưởng tượng một chuỗi vô hạn các nửa liên tiếp của nó.Tuyên bố LINQ cho một chuỗi vô hạn các nửa liên tiếp

1, 0.5, 0.25, 0.125, ... 

(Bỏ qua bất kỳ không ổn định bằng số vốn có trong double.)

này có thể được thực hiện trong một biểu thức duy nhất mà không cần viết bất kỳ phương pháp mở rộng thông thường hoặc các phương pháp phát?

+4

Tại sao những hạn chế tùy tiện? Đây có phải là bài tập về nhà không? Nếu bạn không cam kết với các giới hạn được thể hiện của mình, chỉ cần viết một trình lặp bằng cách sử dụng 'yield'' return'. –

+5

Chuỗi vô hạn và Máy tính không hoạt động cùng nhau. –

+6

@Ramhound Chắc chắn họ làm, miễn là bạn không cố gắng để có được tất cả các mặt hàng. – hvd

Trả lời

10

Tôi không biết một cách đơn thể hiện nhưng tôi tìm thấy mã máy phát điện thông minh này ở đây: http://csharpindepth.com/articles/Chapter11/StreamingAndIterators.aspx

public static IEnumerable<TSource> Generate<TSource>(TSource start, 
                Func<TSource,TSource> step) 
{ 
    TSource current = start; 
    while (true) 
    { 
     yield return current; 
     current = step(current); 
    } 
} 

Trong trường hợp của bạn, bạn muốn sử dụng nó:

foreach (double d in Generate<double>(1, c => c/2)) 
{ 
    ... 
} 
2
Enumerable.Repeat(1, int.MaxValue).Select((x, i) => x/Math.Pow(2, i)) 

Nó không phải là thực sự vô hạn, nhưng khi cả hai RepeatSelect sử dụng thực thi hoãn lại, bạn sẽ không mất bất kỳ hiệu suất nào.

Không biết bất kỳ cách nào để tạo biểu thức linq vô hạn.

Hoặc bạn có thể tự viết phiên bản vô hạn của .Repeat

10

Đối với niềm vui, đây là một thủ thuật để tạo ra một chuỗi vô hạn thực trong một biểu thức duy nhất. Hai định nghĩa đầu tiên là các trường lớp, do đó chúng không yêu cầu một biểu thức được khởi tạo.

double? helper; 
IEnumerable<double> infinite; 

infinite = new object[] { null }.SelectMany(dummy => new double[] { (helper = (helper/2) ?? 1).Value }.Concat(infinite)); 
+1

Thật tuyệt vời, theo một cách khủng khiếp :) –

+1

Tôi đã không dự định nó như bất cứ điều gì khác :) – hvd

+2

Tuyệt. Bạn có thể liệt kê điều này trong LinqPad nhưng vì một số lý do nó bị treo nếu bạn cố gắng làm một Count() trên nó; o) –

0

Tôi không biết cách nào để tạo chuỗi vô hạn bằng LINQ thẳng. Tuy nhiên, bạn có thể tạo một chuỗi rất dài.

var sequence = Enumerable.Range(0, int.MaxValue) 
         .Select(n => Math.Pow(2, -n)); 

Tuy nhiên, vì double có độ chính xác hữu hạn, có thể bạn sẽ nhận được gì nhưng số không sau n trở nên quá cao. Bạn sẽ phải thử nghiệm để xem điều gì sẽ xảy ra và mức độ cao như thế nào n có thể đạt được trước đó.

3

Dưới đây là một câu trả lời tương tự như một @hvd cung cấp, nhưng sử dụng các nhà điều hành Y định nghĩa here, điều này loại bỏ sự cần thiết của các biến địa phương:

public static Func<A, R> Y<A, R>(Func<Func<A, R>, Func<A, R>> f) 
{ 
    return t => f(Y(f))(t); 
} 

var halves = Y<double, IEnumerable<double>>(self => d => new[] { 0d }.SelectMany(_ => new[] { d }.Concat(self(d/2)))); 

Một sử dụng ví dụ sẽ là:

foreach (var half in halves(20)) 
    Console.WriteLine(half); 

Điều gì sẽ xuất 20, 10, 5, 2.5 v.v.

Tôi sẽ không khuyên bạn sử dụng mã này trong mã sản xuất nhưng thật thú vị.

Nhà điều hành Y cũng cho phép biểu thức lambda đệ quy khác, ví dụ:

var fibonacci = Y<int, int>(self => n => n > 1 ? self(n - 1) + self(n - 2) : n); 
var factorial = Y<int, int>(self => n => n > 1 ? n * self(n - 1) : n); 
var hanoi = Y<int, int>(self => n => n == 1 ? 1 : 2 * self(n - 1) + 1); 
+0

Thật gọn gàng. Nó sử dụng hai biểu thức, nhưng chỉ có thể hàm 'Y' đã được định nghĩa ở đâu đó, trong trường hợp đó bạn có thể tham chiếu và tránh định nghĩa của nó. – hvd

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