ElvisLives / morelinq

Automatically exported from code.google.com/p/morelinq
Apache License 2.0
0 stars 0 forks source link

Adding series generation/comprehension methods #5

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
Consider adding series generation/comprehension methods for programmatic
generation of collections "from scratch".

Not sure this would not technically be called a LINQ operator, but it is
very useful for generating data permutations more complex than a 1-step
range (as with Enumerable.Range), for tests or otherwise, as well as
generating ordinals to be paired with collection items using the Zip operator.

The method would have the following behavior given fairly vanilla generator
and terminal functions:

Series.Expand(2, n => n * n, n > 1000) => (2, 4, 8, 16, 32, 64, 128, 256, 512)

The method would have the following behavior given a vanilla generator
function and an element count:

Series.Expand(1, n => n + 3, 8) => (1, 4, 7, 10, 13, 16, 19, 22)

The method would generate new elements lazily and stream them.  This would
also allow an effectively "infinite" sequence to be generated, for example
for the purposes of Zipping with a collection of indeterminate length.

I have attached a patch with a proposed implementation and tests.  I placed
the method in its own file and static class because it is not strictly an
operator like the others.

Original issue reported on code.google.com by CAmmerman@gmail.com on 16 Feb 2009 at 2:55

Attachments:

GoogleCodeExporter commented 9 years ago
I like the idea of a Generate method (and I've written one before) but I don't 
think 
I'd specify either a condition or a number of elements to generate - using Take 
and 
TakeWhile do that well enough, I think.

I haven't looked at the patch yet - will try to do so tonight.

Original comment by jonathan.skeet on 16 Feb 2009 at 7:34

GoogleCodeExporter commented 9 years ago
We can borrow from Python's (x)range here and add a Range operator:

Range(int stop)
Range(int start, int stop)
Range(int start, int stop, int step)

I can also imagine a default, zero-arguments, overload of Range that produces 
the 
full series of integers/longs. With this in place, one can use Select and other 
existing operators to the same effect. For example:

Series.Expand(2, n => n * n, n > 1000) => (2, 4, 8, 16, 32, 64, 128, 256, 512)

becomes:

Range<int>().Select(n => n + 2)
            .Select(n => n * n)
            .TakeWhile(n => n < 1000)

That can be a bit mouthful and verbose so I can understand CAmmerman's point to 
have 
something that simplifies a very common use case. After all, many base LINQ 
operators also have overloads for projection (see Max, for example) when there 
is 
already Select. However, I would stard with Range because that can serve as a 
basis 
for Expand.

Original comment by azizatif on 16 Feb 2009 at 9:15

GoogleCodeExporter commented 9 years ago
OK, I'm silly and it had momentarily slipped my mind that Range already exists:
http://msdn.microsoft.com/en-us/library/system.linq.enumerable.range.aspx

So we are now talking about:

Range(2, int.MaxValue).Select(n => n * n).TakeWhile(n => n < 1000)

Original comment by azizatif on 16 Feb 2009 at 9:51

GoogleCodeExporter commented 9 years ago
> but I don't think I'd specify either a condition or a number of elements to 
generate

I was thinking along the lines of what azizatif was saying, about simplifying 
the
common use cases.  I'm not married to it however.  And we could certainly have 
an
overload without the terminator condition or element count.  On a related note, 
the
terminal check might do better if it worked like TakeWhile, rather than "take 
until".
 The code itself is older, and I don't remember why I chose to do it this way originally.

There might be a better name for this method than Expand, as well.  It was just 
the
simplest thing that applied, in my mind at the time.

Original comment by CAmmerman@gmail.com on 16 Feb 2009 at 1:20

GoogleCodeExporter commented 9 years ago
My example (which has since been repeated by Aziz) was not quite right....  The 
n * n
should be n * 2, to generate the sequence I specified.  n * n would be repeated
squaring, which would overflow quite rapidly. =)

Series.Expand(2, n => n * 2, n > 1000) => (2, 4, 8, 16, 32, 64, 128, 256, 512)

The patch I submitted has the same typo in the comments.

Original comment by CAmmerman@gmail.com on 16 Feb 2009 at 3:18

GoogleCodeExporter commented 9 years ago
If we're looking at Range, I have a much more competent Range class in 
MiscUtil. I'm 
not sure whether I want to bring that over to MoreLinq though - it's useful 
outside 
LINQ too, and I'd rather keep to operators.

@Comment 5: That example still isn't right - it should be:

Series.Expand(2, n => n * 2, n => n > 1000)

which I'd still prefer to see as:

Series.Expand(2, n => n * 2).TakeWhile(n => n <= 1000)

Generating a sequence and terminating it are somewhat orthogonal concepts, and 
the 
latter already exists in a well-known form. I think I'd find the latter easier 
to 
read, aside from anything else.

Original comment by jonathan.skeet on 17 Feb 2009 at 6:31

GoogleCodeExporter commented 9 years ago
@CAmmerman: You should commit your patch. I agree with Jon, though, to keep the 
terminating condition out for now. It can always be added later in version 2. 
Meanwhile it does not prevent anyone from having their own overload that 
combines 
Expand with TakeWhile or Expand with Take to get your original three-part 
Expand 
version. At least, they'll be getting a tested base to build on.

Original comment by azizatif on 17 Feb 2009 at 11:32

GoogleCodeExporter commented 9 years ago
Implemented in r38.

Original comment by azizatif on 18 Feb 2009 at 9:31