简体   繁体   中英

LINQ query and sub-query enumeration count in C#?

suppose I have this query :

  int[] Numbers= new int[5]{5,2,3,4,5};

  var query =  from a in Numbers
      where a== Numbers.Max (n => n) //notice MAX  he should also get his value somehow
      select a;

foreach (var element in query)
  Console.WriteLine (element);
  • How many times does Numbers is enumerated when running the foreach ?

  • how can I test it ( I mean , writing a code which tells me the number of iterations)

Here is how you can check the count

void Main()
{
    var Numbers= new int[5]{5,2,3,4,5}.Select(n=>
    {
       Console.Write(n);
       return n;
    });

    var query =  from a in Numbers
                 where a== Numbers.Max (n => n)
                 select a;

    foreach (var element in query)
    {
          var v = element;
    }
}

Here is output

5 5 2 3 4 5 2 5 2 3 4 5 3 5 2 3 4 5 4 5 2 3 4 5 5 5 2 3 4 5  

It will be iterated 6 times. Once for the Where and once per element for the Max .

The code to demonstrate this:

private static int count = 0;
public static IEnumerable<int> Regurgitate(IEnumerable<int> source)
{
    count++;
    Console.WriteLine("Iterated sequence {0} times", count);
    foreach (int i in source)
        yield return i;
}

int[] Numbers = new int[5] { 5, 2, 3, 4, 5 };

IEnumerable<int> sequence = Regurgitate(Numbers);

var query = from a in sequence
            where a == sequence.Max(n => n)
            select a;

It will print "Iterated sequence 6 times".

We could make a more general purpose wrapper that is more flexible, if you're planning to use this to experiment with other cases:

public class EnumerableWrapper<T> : IEnumerable<T>
{
    private IEnumerable<T> source;
    public EnumerableWrapper(IEnumerable<T> source)
    {
        this.source = source;
    }

    public int IterationsStarted { get; private set; }
    public int NumMoveNexts { get; private set; }
    public int IterationsFinished { get; private set; }

    public IEnumerator<T> GetEnumerator()
    {
        IterationsStarted++;

        foreach (T item in source)
        {
            NumMoveNexts++;
            yield return item;
        }

        IterationsFinished++;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public override string ToString()
    {
        return string.Format(
@"Iterations Started: {0}
Iterations Finished: {1}
Number of move next calls: {2}"
, IterationsStarted, IterationsFinished, NumMoveNexts);

    }
}

This has several advantages over the other function:

  1. It records both the number of iterations started, the number of iterations that were completed, and the total number of times all of the sequences were incremented.
  2. You can create different instances to wrap different underlying sequences, thus allowing you to inspect multiple sequences per program, instead of just one when using a static variable.

Here is how you can estimate a quick count of the number of times the collection is enumerated: wrap your collection in a CountedEnum<T> , and increment counter on each yield return , like this --

static int counter = 0;

public static IEnumerable<T> CountedEnum<T>(IEnumerable<T> ee) {
    foreach (var e in ee) {
        counter++;
        yield return e;
    }
}

Then change your array declaration to this,

var Numbers= CountedEnum(new int[5]{5,2,3,4,5});

run your query, and print the counter . For your query, the code prints 30 ( link to ideone ), meaning that your collection of five items has been enumerated six times.

The number of iteration has to be equal to query.Count() .

So to the count of the elements in the result of the first query.

If you're asking about something else, please clarify.

EDIT

After clarification:

if you're searching for total count of the iteration in the code provided, there will be 7 iterations (for this concrete case).

var query =  from a in Numbers
      where a== Numbers.Max (n => n) //5 iterations to find MAX among 5 elements
      select a;

and

foreach (var element in query)
  Console.WriteLine (element); //2 iterations over resulting collection(in this question)

How many times does Numbers is enumerated when running the foreach

Loosely speaking, your code is morally equivalent to:

foreach(int a in Numbers)
{
   // 1. I've gotten rid of the unnecessary identity lambda. 
   // 2. Note that Max works by enumerating the entire source.
   var max = Numbers.Max();

   if(a == max)
     Console.WriteLine(a);
}

So we enumerate the following times:

  1. One enumeration of the sequence for the outer loop ( 1 ).
  2. One enumeration of the sequence for each of its members ( Count ).

So in total, we enumerate Count + 1 times.

You could bring this down to 2 by hoisting the Max query outside the loop by introducing a local.

how can I test it ( I mean , writing a code which tells me the number of iterations)

This wouldn't be easy with a raw array. But you could write your own enumerable implementation (that perhaps wrapped an array) and add some instrumentation to the GetEnumerator method. Or if you want to go deeper, go the whole hog and write a custom enumerator with instrumentation on MoveNext and Current as well.

Count via public property also yields 6.

private static int ncount = 0;
private int[] numbers= new int[5]{5,2,3,4,5};
public int[] Numbers 
{ 
    get
    {
        ncount++;
        Debug.WriteLine("Numbers Get " + ncount.ToString());  
        return numbers;
    }
}

This brings the count down to 2.
Makes sense but I would not have thought of it.

int nmax = Numbers.Max(n => n);
var query = from a in Numbers
    where a == nmax //notice MAX  he should also get his value somehow
    //where a == Numbers.Max(n => n) //notice MAX  he should also get his value somehow
select a;

It will be iterated 6 times. Once for the Where and once per element for the Max .

Define and initialize a count variable outside the foreach loop and increment the count variable as count++ inside the loop to get the number of times of enumeration.

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