Оператор Linq для бесконечной последовательности последовательных половинок

Учитывая начальный номер, представьте бесконечную последовательность его последовательных половин.

1, 0.5, 0.25, 0.125, ...

(Игнорировать любые численные нестабильности, присущие double.)

Можно ли это сделать в одном выражении без написания пользовательских методов расширения или методов генератора?

5 ответов

Решение

Я не знаю способа с одним выражением, но я нашел этот умный генераторный код здесь: 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);
   }
}

В вашем случае вы бы использовали это:

foreach (double d in Generate<double>(1, c => c / 2))
{
    ...
}

Для забавы вот трюк, чтобы создать реальную бесконечную последовательность в одном выражении. Первые два определения являются полями класса, поэтому они не требуют инициализации выражения.

double? helper;
IEnumerable<double> infinite;

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

Вот ответ, аналогичный приведенному @hvd, но с использованием Y оператор, определенный здесь, устраняет необходимость в локальных переменных:

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

Пример использования будет:

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

Что бы вывести 20, 10, 5, 2.5 и т. Д.

Я бы не советовал использовать это в рабочем коде, но это весело.

Y Оператор также допускает другие рекурсивные лямбда-выражения, например:

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);
Enumerable.Repeat(1, int.MaxValue).Select((x, i) => x / Math.Pow(2, i))

Это на самом деле не бесконечно, а как Repeat а также Select используйте отложенное выполнение, вы не потеряете производительность.

Не знаю ни одного родного способа создания бесконечного выражения linq.

Или вы можете вручную написать бесконечную версию .Repeat

Я не знаю ни одного способа сделать бесконечную последовательность с прямым LINQ. Но вы могли бы сделать очень длинную последовательность.

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

Тем не менее, так как double имеет конечную точность, вы получите только нули после n становится слишком высоким.

Другие вопросы по тегам