Оператор 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 оператор также допускает другие рекурсивные лямбда-выражения, e.g:

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 становится слишком высокой.