What determines if a variable is closed?

//var sample= new { bottom=2, top=5,count=4}; var sample= new [] {2,3,4,5,6}; var q = from x in new int[]{0} //force desired behavior from i in sample from j in sample select Math.Pow(i,j); q.Distinct().Count().Dump(); sample = new[]{2,3,4,5}; //q = from i in Enumerable.Range(sample.bottom, sample.top-sample.bottom+1) // from j in Enumerable.Range(sample.bottom, sample.top-sample.bottom+1) // select checked(System.Numerics.BigInteger.Pow((BigInteger)i,j)); q.Distinct().Count().Dump(); 

The 2nd answer is incorrect every time if from x line does not exist, or the q variable is not reset. (shown here, commented out)

The original pattern was an anonymous variable, but the array did this as well.

var sample = new { bottom =2, top=5};

Does this relate to this? The volume of Linq expressions defined in a loop - In the question: closing a loop loop variable ?

Why put 1 array element on top to fix the closure?

+4
source share
2 answers

The first step in understanding what is going on here is understanding how the query expression is expressed. According to specification:

 from x1 in e1 from x2 in e2 select v 

translates to

 e1.SelectMany(x1 => e2, (x1, x2) => v) 

This is incredibly important to understand. The result from x1 in e1 from x2 in e2 select v is a request for e1 , regardless of e1 ; it is fixed, period.

Now, here is the problem. Look at your request.

 var q = from i in sample from j in sample select Math.Pow(i, j) 

The sample value at the time of constructing the query is a reference to the result of the array creation expression new[] { 2, 3, 4, 5, 6 } . Let e1 be a reference to the result of this array creation expression. Then the query will look like this:

 e1.SelectMany(i => sample, (i, j) => Math.Pow(i, j)); 

This is a fixed period, regardless of what you reassign to sample . That is, you should think about

 var sample = new[] { 2, 3, 4, 5, 6 } var q = from i in sample from j in sample select Math.Pow(i, j) 

a

 var sample = new[] { 2, 3, 4, 5, 6 }; var e1 = sample; var q = e1.SelectMany(i => sample, (i, j) => Math.Pow(i, j)); 

Now the sample closed, so after you assign a new value to sample , you will see a different query result. But no matter what , e1 remains fixed as the result of the array creation expression new[] { 2, 3, 4, 5, 6 } .

However, when you start with a request

 var q = from x in new int[] { 0 } from i in sample from j in sample select Math.Pow(i, j) 

now the request is translated into

 int[] e1 = new int[] { 0 }; var q = e1.SelectMany( x1 => sample.SelectMany(i => sample, (i, j) => Math.Pow(i, j) ); 

and now sample is fixed as a sequence for i and j , but e1 fixed.

+2
source

This code closes over sample twice.

 from x in new int[]{0} from i in sample from j in sample select Math.Pow(i,j); 

The actual method calls for which this is evaluated:

 new int[] {0} .SelectMany(x => sample, (x, i) => new {x, i}) .SelectMany(@t => sample, (@t, j) => Math.Pow(@ti, j)); 

This code closes through sample once.

 from i in sample from j in sample select Math.Pow(i,j); 

The actual method calls for which this is evaluated:

 sample.SelectMany(i => sample, (i, j) => Math.Pow(i, j)); 

So, if you change the sample , only i => sample lambda will "see" the "new" value for sample ; sample.SelectMany already works with the "old" value.

Both examples cause R # to issue a warning of access to a modified closure, which is usually a large red flag. Consider storing a private copy of sample for each of your queries, or for a function parameter do this for you:

 IEnumerable<int> SelectManyPow(int[] sample) { return from i in sample from j in sample select Math.Pow(i, j); } 
+2
source

Source: https://habr.com/ru/post/1495064/


All Articles