Reputation: 31
I'm fairly new to programming and I've been playing around with writing some random functions.
I wrote the below function which works loosely based on eratosthenes sieve. Initially though, I was having a problem with the updatedEntries IEnumerable.
The updatedEntites was sort of populating (something to do with deferred execution I gather - in debug mode the 'current' was null but the results view contained the relevant items) but when the RemoveWhere was applied to oddPrimesAndMultiples the items in updatedEntries disappeared even though I don't see why they should still be linked to the items in oddPrimesAndMultiples. (I could just be completely misunderstanding what's going on of course and the problem might be something else entirely!)
The problem doesn't arise if I change updatedEntries to a List rather than an IEnumerable and I've actually now rewritten that statement without using LINQ to (potentially?) make better use of the fact I'm using a SortedSet anyway...but I would still like to know why the issue arose in the first place!
Here is my code:
public static IEnumerable<int> QuickPrimes()
{
int firstPrime = 2;
int firstOddPrime = 3;
int currentValue = firstOddPrime;
int currentMinimumMultiple;
SortedSet<Tuple<int, int>> oddPrimesAndMultiples = new SortedSet<Tuple<int, int>>() { new Tuple<int, int> (firstOddPrime, firstOddPrime) };
IEnumerable<Tuple<int, int>> updatedEntries;
yield return firstPrime;
yield return firstOddPrime;
while (true)
{
currentMinimumMultiple = oddPrimesAndMultiples.First().Item1;
while (currentValue < currentMinimumMultiple)
{
yield return currentValue;
oddPrimesAndMultiples.Add(new Tuple<int, int> (currentValue * 3, currentValue));
currentValue += 2;
}
updatedEntries = oddPrimesAndMultiples.Where(tuple => tuple.Item1 == currentMinimumMultiple)
.Select(t => new Tuple<int, int>(t.Item1 + 2 * t.Item2, t.Item2));
oddPrimesAndMultiples.RemoveWhere(t => t.Item1 == currentMinimumMultiple);
oddPrimesAndMultiples.UnionWith(updatedEntries);
currentValue += 2;
}
}
and the main where I'm testing the function:
static void Main(string[] args)
{
foreach(int prime in Problems.QuickPrimes())
{
Console.WriteLine(prime);
if (prime > 20) return;
}
}
Many thanks in advance!
Upvotes: 3
Views: 283
Reputation: 109080
The trap is that updatedEntries
is defined in one line, but actually executed later.
To bring it back to the basics, see this code snippet (from Linqpad):
var ints = new SortedSet<int>( new[] { 1,2,3,4,5,6,7,8,9,10});
var updatedEntries = ints.Where(i => i > 5); // No ToList()!
updatedEntries.Dump();
This shows 6, 7, 8, 9, 10
.
ints.RemoveWhere(i => i > 7);
updatedEntries.Dump();
Now this shows 6, 7
, because updatedEntries
is re-executed.
ints.UnionWith(updatedEntries);
This adds 6, 7
, while you expected it to add the first listing 6, 7, 8, 9, 10
.
So when defining an IEnumerable
you should always be aware of when it's actually executed. It always acts on the state of the program at that particular point.
Upvotes: 1