简体   繁体   中英

How to convert a for (not foreach) loop to Linq?

Let's say I want a sequence of 10 numbers, and I have a function which produces numbers on demand:

var s = new List<int>();
for (var i=0; i<10; i++) {
    s.Add(Magically_generate_a_very_special_number());
}

Is the usual way of accomplishing this. However, let's say I want to use LINQ. I can already do (let's ignore the distinction between types):

var s = Enumerable.Range(0, 10).Select(i => MathNet.Numerics.Statistics.DescriptiveStatistics())

Which is almost good enough for me. However, it bothers me that I need to specify a range of integers first - this is a superfluous step since I discard the value anyway. Is it possible to do something like the following?

var s = Enumerable.ObjectMaker(Magically_generate_a_very_special_number).Take(10);

It seems that Enumerable.Repeat almost does what I want, but it takes the result of the function I give it and then duplicates that, instead of repeatedly evaluating the function.

By the way, the inspiration for this question was the Math.Net IContinuousDistribution.Samples method. Its body looks like the following:

while (true)
{
    yield return Generate_a_random_number();
}

So that you can obtain a sequence of samples from the distribution with myDistribution.Samples.Take . In principle, I could just write my own method to produce an iterator in the same way, but I'm wondering if there is one that already exists.

You could try using the foreach method :

Enumerable.Take(10).ToList().Foreach(Magically_generate_a_very_special_number);

Downside imho is that you have to do a ToList in between.

edit misread question, so nvmd :)

您可以创建一个包含10个方法委托的序列,这些委托引用您的Magically_generate_a_very_special_number方法,然后连续调用它们。

var s = Enumerable.Repeat<Func<int>>(generate, 10).Select(f => f());

I don't think anything like your Enumerable.ObjectMaker exists as a convenient part of LINQ, but you can make one exactly like it.

public static IEnumerable<T> ObjectMaker<T>(Func<T> generator) {
    while (true)
        yield return generator();
}
var s = ObjectMaker(MagicallyGenerateVerySpecialNumber).Take(10);

I agree with danish: I think trying to do it in base linq is uglier and harder to read than just doing it in a for loop. Seems to defeat the purpose of linq.

That said, maybe you can make an extension method. So long as your Magically_generate_a_very_special_number function doesn't require parameters, something like this should do what you're after:

public static void Fill<T>(this IList<T> list, int repeat, Func<T> fn) {
    for (int i = 0; i < repeat; i++) list.Add(fn());
}

Then use like this:

s.Fill(10, Magically_generate_a_very_special_number);

We can create a new method to generate N objects by calling a generation function N times that follows the general pattern of LINQ methods, so that you have something that fits your exact needs:

public static IEnumerable<T> Generate<T>(Func<T> generator, int count)
{
    for (int i = 0; i < count; i++)
        yield return generator();
}

And now you can write:

var numbers = Generate(Magically_generate_a_very_special_number, 10);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM