-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Enumerable.Repeat overload accepting a Func. #19454
Comments
Related proposal and discussion @ https://github.com/dotnet/corefx/issues/5078. One possible concern with the overloads proposed is that a closed-over resource used in the Symmetry with the |
I'm not sure the combination of Compare this with F#, which has the two functions public static IEnumerable<T> init<T>(int count, Func<int, T> initializer);
public static IEnumerable<T> unfold<T, State>(
Func<State, Option<Tuple<T, State>>> generator, State state); Notice that I think this reflects more closely the cases where this kind of generating a sequence is useful:
Case 1 is not an issue, but I think the proposed design of So, I would instead suggest a design that follows public static IEnumerable<T> Repeat<T>(Func<int, T> selector, int count); I would probably leave serving case 3 to a separate proposal, though I think it could look like this (not sure about the name): public static IEnumerable<T> Unfold<T, TState>(
TState seed, Func<TState, (T value, TState state)? generator); Or maybe: public static IEnumerable<T> Unfold<T>(T seed, Func<T, (T value, bool end) generator); |
Did we abandon this proposal? It's already 2 years old and I see no new discussion over here. Is there something limiting us from going ahead with a final proposal for this one? These methods are really useful and I've needed this exact behavior multiple times and ended up creating extensions myself to do it. I do not think a I agree with @svick that the initial proposal is a bit too barebones/not generic enough, but would suggest using |
I actually do think it's a fair substitute. The goal of The two main examples motivating this provided were: // Numbers from 100 to 1
Enumerable.Repeat(100, n => n - 1, 100);
// Record the results of a benchmark 10 times
Enumerable.Repeat(_ => RunBenchmark(), 10); That can be accomplished with Enumerable today with: // Numbers from 100 to 1
Enumerable.Range(0, 100).Select(n => 100 - n)
// Record the results of a benchmark 10 times
Enumerable.Range(0, 10).Select(_ => RunBenchmark()); The third example of gathering the nodes of a list doesn't make sense to me as a "Repeat" method. New APIs have significant overhead, not just in terms of implementing, testing, maintaining, documenting, etc. them, but also in terms of developers seeing them, needing to learn what they mean (personally the proposed overload that takes three arguments is very confusing to me at the call site), needing to decide which option they need, and so on. This does not meet the bar for me. |
It's one thing to compose a solution to a problem out of steps that are clear in their intent and make sense as contributing to the final output. It's another thing to force a developer to generate garbage data simply to take advantage of the side-effect that it produces a sequence of the desired length to iterate over. If you simply wanted to execute a function 10 times, you wouldn't do this:
instead of
as the former seems at a glance to imply there is some other purpose for the int array outside of the foreach loop. Likewise, if all you want is to use the cleaner, Linq syntax to create a sequence of 10 unique items, doing this:
seems to imply you are somehow using a sequence of consecutive numbers to generate your items when actually, you're just throwing those numbers away. Whereas this is much more clear in its intent:
Another alternative to using Enumerable.Range is just to use the existing Repeat method in the same way:
But the problem is the same. You're generating garbage data that has nothing to do with the goal you are trying to accomplish other than the fact that it's packaged in a sequence that you can use to iterate over. |
|
Actually, its not. You need something to keep track of the number of times the loop is iterated, and its immediately clear to anyone familiar with the basics of the language that that is the purpose of the variable "i" in the for statement (even if i isn't actually used in the body of the loop). Contrast that with Enumerable.Range(0, 10).Select(...) where the actual contents of the generated integer sequence is useless and only the length of the sequence matters. |
In both cases, there is an iteration variable. In both cases, it's available to the work being performed for each iteration. In both cases, it's ignored. |
Its about clarity of intent. In one case the intent is immediately clear, in the other its not. |
We will have to agree to disagree then. I believe the intent here is clear. |
Background
The current
Enumerable.Repeat
function, accepting aT
, returns an enumerable with the same value repeated N times. Sometimes, however, you don't want the same value repeated N times but the same function called N times. For example:The current workaround for this is to do something like
Enumerable.Range(0, size).Select(_ => r.Next())
, which is not really as readable. I do it myself all the time in my benchmarks for example, & you can see such examples on StackOverflow.Proposal
We should add an overload of
Repeat
accepting aFunc<T, T>
. This function will be evaluated N times as the enumerable is iterated. The input to each invocation of the function will be the result of the last invocation; the input to the first invocation will either be a user-specified seed, ordefault(T)
if no seed was specified.Usage
The text was updated successfully, but these errors were encountered: