1

I was doing some work by my own for upcoming test and found this question that I could not understand.

int[] div = new int[]{2,3,5};
IEnumerable<int>seq = new int[]{10,15,20,25,30};

for(int i = 0; i<div.Length;i++){
    int y = div[i];
    seq = seq.Where(s => s%y == 0)
}
seq = seq.ToList();

I thought the resulting sequence would be 10 15 20 25 30 but the actual answer was 30.

I tried to run the code by my self and I found that when the code runs the for-loop, the sequence does not resets to the original but it keeps the sequence created by the previous div[i].

For example, when i = 0, div[0] is 2 so the seq picks the 10, 20 and 30.

As the code proceeds to i = 1 and div[1] = 3, the sequence it uses for the calculation part is still {10,20,30} not {10,15,20,25,30}.

Can someone explain why?

When I move int y to the outside of the for-loop like this,

int[] div = new int[]{2,3,5};
IEnumerable<int>seq = new int[]{10,15,20,25,30};
int y;

for(int i = 0; i<div.Length;i++){
    y = div[i];
    seq = seq.Where(s => s%y == 0)
}
seq = seq.ToList();

it gives the answer I was expecting.

Any help will be really helpful.

Thank you.

Konamiman
  • 49,681
  • 17
  • 108
  • 138
Andy Min
  • 91
  • 1
  • 6
  • 1
    That's because you are overriding `seq` on every iteration. So you "search" in a short list every time. – frikinside Nov 06 '15 at 08:11
  • Why did you expect 10 to be in the result? 10 doesn't meet `s % 3 == 0` which is one of the filters you applied. What are you actually *trying* to achieve here? I know exactly why the code works the way it does, but it's not clear why you expected it to work differently, or what the intention was. – Jon Skeet Nov 06 '15 at 08:11
  • Please write up a textual description of what you wanted the code to do, instead of just "I want this result". The shortest possible code to give the results you wanted is actually line 2, declaring `seq`. The question is why you added the rest of the code? – Lasse V. Karlsen Nov 06 '15 at 08:27

2 Answers2

6

This is the resulting query you end up with:

IEnumerable<int> seq = new int[]{10,15,20,25,30};
seq = seq
    .Where(s => s%2 == 0)
    .Where(s => s%3 == 0)
    .Where(s => s%5 == 0);

So, every number that is divisible by 2, 3 and 5:

10 is not divisible by 3
15 is not divisible by 2
20 is not divisible by 3
25 is not divisible by 2
30 is divisible by 2 (15), 3 (10), and 5 (6)

So 30 is the only result.


Now, if you instead want this query: "I want every number in seq that is divisible by 2, 3 or 5", then you need to do it different.

The code you had will filter every step of the way, only letting through to the next step any numbers that fit, so each will filter, but you want the opposite, if any one of them would say that it was OK, then you want the number.

To get the or expression instead, this would be the code:

seq.Where(s => div.Any(y => s % y == 0))

This gives this result:

10
15
20
25
30

Basically this query says:

Any number s in seq, which for any number y in div, where s is divisible by y.


As for your last part, why did it change after you moved the y outside the loop?

Well, let's see what actually happens.

When you have the y inside the loop, as in the first example, for each iteration through the loop, you can think of y as a "new y just for this loop iteration".

As such, you can think of the code that is being executed producing this query:

int y1 = 2;
int y2 = 3;
int y3 = 5;
seq = seq
    .Where(s => s%y1 == 0)
    .Where(s => s%y2 == 0)
    .Where(s => s%y3 == 0);

But, if you move the y variable outside the loop you only ever have one y, which you change, so the final result is this:

int y = 5; // the last number in the loop
seq = seq
    .Where(s => s%y == 0)
    .Where(s => s%y == 0)
    .Where(s => s%y == 0);

Which basically checks that every number is divisible by 5, 3 times.

So while you say that this gives you the result you want, it doesn't do what you want. Or rather, I assume it doesn't do what you want since you haven't actually described what you wanted the code to do.

Here are some links that explain the differences:

Community
  • 1
  • 1
Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
  • Might be worth adding details about moving the `y` declaration outside of the loop, deferred execution and capturing. – Damien_The_Unbeliever Nov 06 '15 at 08:18
  • Why would you move `y` outside the loop? That part of the code is actually OK, the code will create a "new" `y` on every iteration, and the delegate will capture this new `y`, there is not just one `y` in play here, that is not the problem with the code. – Lasse V. Karlsen Nov 06 '15 at 08:18
  • Because the OPs question says "When I move int y to the outside of the for-loop like this" and then shows a second code sample that exhibits different behaviour. – Damien_The_Unbeliever Nov 06 '15 at 08:19
  • Agh, I didn't read that last part well enough, let me edit it in. – Lasse V. Karlsen Nov 06 '15 at 08:20
  • Thanks, I've edited in some details about the change now. – Lasse V. Karlsen Nov 06 '15 at 08:23
  • Thank you very much. That helped me a lot to understand. The question I asked was from the previous year's test paper. I needed to answer what would be the resulting sequence of two codes. They were only differ in the position of 'int y' and I was not sure why moving the position of it affects the results of two codes. I am sorry if my question was not clear and hard to understand. Thank you again for your help. – Andy Min Nov 06 '15 at 08:37
  • Did my edit at the bottom of my answer that explains the difference between the two help you? If not, please let me know what you're still struggling with and I'll see if I can improve it. – Lasse V. Karlsen Nov 06 '15 at 08:38
  • The important things to read to explain the difference would be [Anonymous Methods](https://msdn.microsoft.com/en-us/library/0yw3tz5k.aspx), [Detailed Explanation of Variable Capture in Closures](http://stackoverflow.com/questions/5438307/detailed-explanation-of-variable-capture-in-closures), [Captured variable in a loop in C#](http://stackoverflow.com/questions/271440/captured-variable-in-a-loop-in-c-sharp). – Lasse V. Karlsen Nov 06 '15 at 08:40
  • yes it really help me to understand. I will make sure to read the pages you recommended as well. – Andy Min Nov 06 '15 at 08:43
1

You code can be simplified by adding some more LINQness.

If what you are trying to get is the values of seq that can be divided by any of the values of div, you can simply do:

seq.Where(s => div.Any(d => s%d == 0));

If you want the values of seq that can be divided by all of the values of div, replace the .Any with .All.

Konamiman
  • 49,681
  • 17
  • 108
  • 138