Superbest
Superbest

Reputation: 26632

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.

Upvotes: 2

Views: 177

Answers (5)

Ry-
Ry-

Reputation: 225281

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

Upvotes: 1

Servy
Servy

Reputation: 203848

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

Upvotes: 0

Ber&#39;Zophus
Ber&#39;Zophus

Reputation: 7697

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

Upvotes: 0

Douglas
Douglas

Reputation: 54917

You could create a sequence of 10 method delegates referencing your Magically_generate_a_very_special_number method, then invoke them consecutively.

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

Upvotes: 1

Kristof
Kristof

Reputation: 3315

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

Upvotes: 2

Related Questions