C# 连续半无限序列的Linq语句

C# 连续半无限序列的Linq语句,c#,linq,sequences,enumerable,infinite-sequence,C#,Linq,Sequences,Enumerable,Infinite Sequence,给定一个起始数字,想象一个连续两半的无限序列 1, 0.5, 0.25, 0.125, ... (忽略double中固有的任何数值不稳定性) 这可以在一个表达式中完成,而不必编写任何自定义扩展方法或生成器方法吗?我不知道一种表达式方式,但我在这里找到了这个巧妙的生成器代码: 公共静态IEnumerable生成(TSource start, Func步骤) { t电源电流=启动; while(true) { 产生回流电流; 电流=阶跃(电流); } } 在您的情况下,您可以使用它: forea

给定一个起始数字,想象一个连续两半的无限序列

1, 0.5, 0.25, 0.125, ...
(忽略
double
中固有的任何数值不稳定性)


这可以在一个表达式中完成,而不必编写任何自定义扩展方法或生成器方法吗?

我不知道一种表达式方式,但我在这里找到了这个巧妙的生成器代码:

公共静态IEnumerable生成(TSource start,
Func步骤)
{
t电源电流=启动;
while(true)
{
产生回流电流;
电流=阶跃(电流);
}
}
在您的情况下,您可以使用它:

foreach (double d in Generate<double>(1, c => c / 2))
{
    ...
}
foreach(Generate(1,c=>c/2)中的双d)
{
...
}
它实际上并不是无限的,但由于
Repeat
Select
都使用延迟执行,所以不会损失任何性能

不知道任何本地方法来创建无限linq表达式


或者您可以手动编写无限版本的
。重复

为了好玩,这里有一个技巧可以在单个表达式中创建真正的无限序列。前两个定义是类字段,因此它们不需要初始化表达式

double? helper;
IEnumerable<double> infinite;

infinite = new object[] { null }.SelectMany(dummy => new double[] { (helper = (helper / 2) ?? 1).Value }.Concat(infinite));
double?帮手;
可数无限;
无限=新对象[]{null}.SelectMany(dummy=>newdouble[]{(helper=(helper/2)??1.Value}.Concat(无限));

我不知道用直LINQ生成无限序列的任何方法。但是你可以做一个很长的序列

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

但是,由于
double
具有有限的精度,因此在
n
变得太高后,除了零之外,什么也得不到。

这里有一个与@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))));
输出20,10,5,2.5等等

我不建议在生产代码中使用它,但它很有趣

Y
运算符还允许使用其他递归lambda表达式,例如:

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);
var-fibonacci=Y(self=>n=>n>1?self(n-1)+self(n-2):n);
var阶乘=Y(self=>n=>n>1?n*self(n-1):n);
var hanoi=Y(self=>n=>n==1?1:2*self(n-1)+1);

为什么要设置任意限制?这是家庭作业吗?如果您没有明确的限制,只需使用
yield
return
编写迭代器即可。无限序列和计算机不能一起工作。@Ramhound当然可以,只要您不尝试获取所有项。标准序列运算符方法中没有提供返回无限序列的方法,你不能从石头上取血;如果没有一个无限序列开始,你就不会从任何序列操作符的后端得到一个无限序列。现在,如果你想要的是一个非常大的序列,比如说,有10亿个项目,当然,这一点都没有问题。取Enumerable.Range任意大,并选择x/Math.Pow(2,i)
作为起始元素。double中的不稳定性适用于表示分母不是2的幂的分数,或分母是2的幂但分子需要超过53位来表示的分数。这里的情况都不是这样,所以在达到double.Epsilon之前,您将得到所有值的精确表示;然后所有后续的值都将为零。这太好了,用一种可怕的方式:)我并没有把它当作其他任何东西:)酷。您可以在LinqPad中枚举它,但由于某种原因,如果您尝试对它进行Count(),它会崩溃;o) @hvd看一看我的答案,另一个有趣的解决方案,它非常基于你的答案,但是使用了
Y
操作符。这很好。它使用了两个表达式,但是有可能
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);
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);