使用Linq进行迭代

3

我希望你能在同一行中找到一种访问Linq方法中先前值的方法。

我想要在Linq中使用这个通用形式:

var values = Enumerable.Range( 1, 100 ).Select( i => i + [last result] );

但我找不到一种方法来做到这一点,而不需要多行lambda和在其他地方存储结果。

所以,我能够在Linq中做到最好的斐波那契数列求和是:

List<int> calculated = new List<int>( new int[] { 1, 2 });
var fibonacci = Enumerable.Range(2, 10).Select(i =>
    {
        int result = calculated[i - 2] + calculated[i - 1];
        calculated.Add(result);
        return result; // and how could I just put the result in fibonacci?
    }
);

这看起来很丑。我可以用普通的for循环写出更少的代码。

for (int i = 2; i < 10; i++)
{
    calculated.Add(calculated[i - 2] + calculated[i - 1]);
}

看起来,如果我能找到一种方法做到这一点,我可以使用Linq来执行大量的线性规划和总结很多迭代公式。


8
LINQ作为首字母缩写的最后一个字母表明它是用于查询的,而不是用于计算。不要把它硬塞到它不属于的领域。 - Oded
@Oded:那可能就是“答案”了。我仍在努力学习Linq的边界。我能想到两次面试,我本应该说出你所说的话(虽然要温和一些)。你能把这个提交为答案吗? - micahhoover
2个回答

6

如果您想创建一个斐波那契数列生成器,最好编写自己的生成器函数,而不是使用Linq扩展方法。可以像这样:

public static IEnumerable<int> Fibonacci()
{
    int a = 1;
    int b = 0;
    int last;

    for (;;) {
        yield return a;

        last = a;
        a += b;
        b = last;
    }
}

那么,您可以对该可枚举对象应用Linq方法来实现所需的结果(例如尝试迭代Fibonacci().Take(20))。

Linq扩展方法并不是每个编程问题的解决方案,我只能想象一个纯LINQ Fibonacci序列生成器会有多糟糕。


我认为使用生成器模式来生成无限序列并不是把方形钉子放进圆孔里。事实上,它可以非常有用。 - cdhowie
2
Howie - 我的评论是关于最后一句话,特别是“Linq扩展方法并不是解决每个编程问题的方法。”。我表示同意。 - Oded
1
现在,如果我们能用更少的语法编写这些生成器就好了。 - ChaosPandion
我会把最后一句话移到一个新段落中。它有点突兀。 - ChaosPandion

1

使用LINQ,最接近这种功能的方法是IEnumerable.Aggregate(也称为函数式编程的折叠)。 例如,您可以使用它来对集合的平方求和,如:

int sumSquares = list.Aggregate(0, (sum, item) => sum + item * item);

在LINQ中,值是使用枚举器从集合中检索的,即它们逐个获取,根据定义,没有“上一个项目”的概念。这些项甚至可以使用一些yield return魔法动态生成和丢弃。话虽如此,您始终可以使用一些技巧,例如:

long a= 1;
long b= 1;
var fibonacci = Enumerable.Range(1,20).Select(i => {
  long last= a + b;
  b = a;
  a = last;
  return last;
});

但是一旦你必须使用和修改外部变量来使lambda函数工作,你就进入了代码异味的领域。


出于某种原因,我不喜欢他们选择了“aggregate”这个名称。不知怎么的,这让结果表达式在我看来变得不太优雅。 - ChaosPandion

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接