How do you capture iteration variables? - c#

When you capture the iteration variable of a for loop, C# treats that variable as though it was declared outside the loop. This means that the same variable is captured in each iteration. The following program writes 333 instead of writing 012:
Action[] actions = new Action[3];
for (int i = 0; i < 3; i++)
actions [i] = () => Console.Write (i);
foreach (Action a in actions) a(); // 333
I'm reading C# in a Nutshell (5th Edition) and today i came across this but i can't get my head over it, i don't get why the output is 333 and not 012. Is it because the value of i that's getting printed is the value after the loop? How is that possible? i is supposed to be disposed after the loop, isn't it?

The variable i is captured inside the for loop but your are kind of extending the scope of it by doing so. So the variable is left at it's last state which was 3, hence the code outputting 333.
Another way to write the code is this:
Action[] actions = new Action[3];
int i; //declare i here instead of in the for loop
for (i = 0; i < 3; i++)
actions [i] = () => Console.Write (i);
//Now i=3
foreach (Action a in actions) a(); // 333
The output is the same as writing:
Console.Write(i);
Console.Write(i);
Console.Write(i);

Because the lambda captures last value of i, and that is 3.Step of your loop is executed for last time,then i becomes 3 and your loop ends.
I think this would make it clear for you:
int i = 0;
for (; i < 3; i++) { }
Console.WriteLine(i); // writes 3
You could fix this using a temporary variable:
for (int i = 0; i < 3; i++)
{
int temp = i;
actions[i] = () => Console.Write(temp);
}
foreach (Action a in actions) a(); // now: 012
I would recommend you to read this article to understand closures better

My approach for understanding the closure in this case is to unroll the for loop:
var actions = new List<Action>();
// this loop is "executed"
for (int i = 0; i < 3; i++)
{
actions.Add(() => Console.Write (i));
}
// pseudo "unroll" the loop
// i = 0
// action(0) = Console.WriteLine(i);
// i = 1
// action(1) = Console.WriteLine(i);
// i = 2
// action(2) = Console.WriteLine(i);
// i = 3
foreach (Action a in actions)
{
a();
}
// pseudo "unroll" the foreach loop
// a(0) = Console.WriteLine(3); <= last value of i
// a(1) = Console.WriteLine(3); <= last value of i
// a(2) = Console.WriteLine(3); <= last value of i
// thus output is 333
// fix
var actions = new List<Action>();
// this loop is "executed"
for (int i = 0; i < 3; i++)
{
var temp = i;
actions.Add(() => Console.Write (temp));
}
// pseudo "unroll"
// i = 0
// temp = 0
// actions(0) => Console.WriteLine(temp); <= temp = 0
// i = 1
// temp = 1
// actions(1) => Console.WriteLine(temp); <= temp = 1
// i = 2
// temp = 2
// actions(2) => Console.WriteLine(temp); <= temp = 2
foreach (Action a in actions)
{
a();
}
// pseudo "unroll" the foreach loop
// a(0) = Console.WriteLine(0); <= last value of first temp
// a(1) = Console.WriteLine(1); <= last value of second temp
// a(2) = Console.WriteLine(2); <= last value of third temp
// thus 012

Related

How to iterate wIth one loop or no loop in elegant way using LINQ / Divide and Conquer method

1
12
123
1234
12345
I had tried with 2 FOR loops to achieve this and a recursion with one loop as well.
public static void Main(string[] args)
{
DisplayNumberPyramid(1);
}
public static void DisplayNumberPyramid(int i)
{
if(i<=5)
{
for(int j=1;j<=i;j++)
{
Console.Write(j);
}
Console.Write("\n");
DisplayNumberPyramid(i+1);
}
Console.ReadLine();
}
Expect the same output using LINQ/DnC method with one loop and without no loops.
Linq solution
var results = Enumerable.Range(1, 5)
.Select(x => string.Concat(Enumerable.Range(1, x)));
foreach(var result in results)
Console.WriteLine(result);
If you really don't want a foreach
Console.WriteLine(string.Join("\r\n",results));
Or for loops
for (var i = 1; i <= 5; i++)
{
for (var j = 1; j <= i; j++)
Console.Write(j);
Console.WriteLine();
}
Output
1
12
123
1234
12345
Another solution using the new Range Type feature of c#8
output = "12345";
for(var i = 1; i <= 5; i++)
Console.WriteLine(output[0..i]);
or using the Substring method
output = "12345";
for(var i = 1; i <= 5; i++)
Console.WriteLine(output.Substring(0, i));

c# split string in 2. letter by letter

I need split my string in 2, one letter to each variable.
Example: string = "ABCDEFGHIJ"
name1: ACEGI
name2: BDFHJ
I done so far:
var builderM = new StringBuilder();
var builderK = new StringBuilder();
for (int i = 0; i < s.Length; i++)
{
builderM.Append(s[i]);
builderK.Append(s[i++]);
}
txtM.Text = builderM.ToString();
txtK.Text = builderK.ToString();
But its showing same text in the 2.
you should use ++i instead of i++
for (int i = 0; i < s.Length; i++)
{
builderM.Append(s[i]);
if(i + 1 < s.Length) // to prevent IOR exception when count is odd.
builderK.Append(s[++i]); // pre increment.
}
the reason is that i++ is post incremented. that means i gets incremented after the expression therefor s[i++] will give you same item as s[i].
Another approach would be to use LINQ to filter odd and even indices into two strings, something like:
var even = new string(input.Where((c, idx) => idx % 2 == 0).ToArray());
var odd = new string(input.Where((c, idx) => idx % 2 != 0).ToArray());
If performance is not an issue, you can use LINQ:
var name1 = String.Join(String.Empty, str.Where((v, i) => i % 2 == 0));
var name2 = String.Join(String.Empty, str.Where((v, i) => i % 2 == 1));
You can also use the modulus operator (%) to determine if the index is even or odd, and put the even indexes in the first array and the odd indexes in the second one:
for (int i = 0; i < s.Length; i++)
{
if (i % 2 == 0) builderM.Append(s[i]);
else builderK.Append(s[i]);
}
If you'd rather increment the i inside the for body, you have to repeat the check against s.Length (as we do in the for condition). Also, you will need to either move the post-increment to the previous line (so that i is incremented in time), or use a pre-increment:
// Move post-increment to previous line example:
for (int i = 0; i < s.Length; i++)
{
builderM.Append(s[i++]);
if (i < s.Length) builderK.Append(s[i]);
}
// Use a pre-increment example:
for (int i = 0; i < s.Length; i++)
{
builderM.Append(s[i]);
if (++i < s.Length) builderK.Append(s[i]);
}

C# foreach loop take values from outer loop to inner loop

I have the below code.
int[] a = new int[] { 8, 9 };
for(int i=0;i<n;i++)
{
print i;
int z;
//during first iteration
z=8;
during second iteration
z=9;
}
Output should be something like this.
during first iteration i=0 and z=8
during second iteration i=1 and z=9
array a contains 2 elements. N and number of elements in array a will be always same. next my for loop will execute. during first iteration want z value should be 8(first element of array ) and second iteration my z value should be 9. I want to map 1st element of integer array to first iteration of for loop and so on.
try
for (int i = 0; i < a.Length; i++) // or i < n if you want
{
print i;
int z = a[i]; // this line will get value from a one by one, 0, 1, 2, 3 and so on...
}
Edit 1 -
After seeing the comments on the other answer, the array 'a' turns out is a dynamic array which have size n (which is 2)
the revised edition:
int n = 2;
int[] a = new int[n];
string input = null;
for (int i = 0; i < a.Length; i++) // or i < n if you want
{
print i;
input = Console.ReadLine();
try {
a[i] = int.Parse(input);
Console.WriteLine(string.Format(
"You have inputted {0} for the {1} element",
input, i
));
} catch { Console.WriteLine("Non integer input"); i -= 1; }
}
you can try this
int [] a = {8,9};
for(int i=0; i< a.Length; i++)
{
int z = a[i]; //for taking value from array at the specific ith position
Console.WriteLine("i: " + i + " z:" + z);
}
try this
List<int> a = new List<int>();
int n = 2; // you can change it according to your need
for (int i = 0; i < n; i++)
{
string str = Console.ReadLine(); // make sure you enter an integer and conver it
int z = int.Parse(str);
a.Add(z);
}
foreach (int k in a)
{
Console.WriteLine(k);
}

Thread safe pass integer to Action with Task

The following code does not work as I expect it. What am I doing wrong? Output is different on every run. Is there a better way of doing this? Assume action does something more complex than what's below.
Action<int> action = (int m) =>
{
if ((m % 2) == 0)
Console.WriteLine("Even");
else
Console.WriteLine("Odd");
};
const int n = 10;
Task[] tasks = new Task[n];
for (int i = 0; i < n; i++)
{
tasks[i] = Task.Factory.StartNew(() => action(i+1));
}
Task.WaitAll(tasks);
The lambda in your loop is capturing a reference to the same i variable every time through the loop, not its value.
Change your loop to something like:
for (int i = 0; i < n; i++)
{
var j = i;
tasks[i] = Task.Factory.StartNew(() => action(j+1));
}
Note that the output will still be different on every run, but you should get exactly five even and five odd outputs.

Use array of threads

I'm new to threads so it might be an easy one for you, but I've spent some hours trying to figure it out.
Let's say I have a function
public double Gain(List<int> lRelevantObsIndex, ushort uRelevantAttribute)
which needs some time to finish, but is a read only func.
I have an array of ushort[] values, and I want to get the ushort value that achieves the minimum value of the Gain function.
Here is what I've got so far, but it's not working:
lRelevantObsIndex is a read only index.
lRelevantAttributes is the list of ushort values.
//Initialize the threads
double[] aGains = new double[lRelevantAttributes.Count];
Thread[] aThreads = new Thread[lRelevantAttributes.Count];
for (int i = 0; i < lRelevantAttributes.Count; i++)
{
aThreads[i] = new Thread(() => aGains[i] = Gain(lRelevantObsIndex, lRelevantAttributes[i]));
aThreads[i].Start();
}
//Join the threads
for (int i = 0; i < lRelevantAttributes.Count; i++)
aThreads[i].Join();
//The easy part - find the minimum once all threads are done
ushort uResult = 0;
double dMinGain = UInt16.MaxValue;
for (int i = 0; i < lRelevantAttributes.Count; i++)
{
if (aGains[i] < dMinGain)
{
dMinGain = aGains[i];
uResult = lRelevantAttributes[i];
}
}
return uResult;
I know this is a simple multithreading question - but still need your brains since I'm new to this.
This one is somewhat tricky: your for loop uses a modified value here (a so-called access to modified closure)
for (int i = 0; i < lRelevantAttributes.Count; i++)
{
aThreads[i] = new Thread(() => aGains[i] = Gain(lRelevantObsIndex, lRelevantAttributes[i]));
aThreads[i].Start();
}
At the time the thread starts, i will be different in your lambda, accessing a wrong item. Modify your loop as follows:
for (int ii = 0; ii < lRelevantAttributes.Count; ii++)
{
var i = ii; // Now i is a temporary inside the loop, so its value will be captured instead
aThreads[i] = new Thread(() => aGains[i] = Gain(lRelevantObsIndex, lRelevantAttributes[i]));
aThreads[i].Start();
}
This will fix the problem, because lambdas will capture the current value of the temporary variable i on each iteration of the loop.
I'm not sure if this is your problem, but it is a problem:
for (int i = 0; i < lRelevantAttributes.Count; i++)
{
aThreads[i] = new Thread(() => aGains[i] = Gain(lRelevantObsIndex, lRelevantAttributes[i]));
aThreads[i].Start();
}
When a lambda refers to a loop variable, the binding is delayed, so that when your lambda actually runs, it takes the value of i at the time the lambda runs, not the value it had when the lambda was created. To fix this, declare a secondary variable inside the loop, and use that in the lambda:
for (int i = 0; i < lRelevantAttributes.Count; i++)
{
int j = i;
aThreads[i] = new Thread(() => aGains[j] = Gain(lRelevantObsIndex, lRelevantAttributes[j]));
aThreads[i].Start();
}
You can do the same on Task
[Fact]
public void Test()
{
List<Task<int>> tasks = Enumerable.Range(0, 5) //- it's equivalent how many threads
.Select(x => Task.Run(() => DoWork(x)))
.ToList();
int[] result = Task.WhenAll(tasks).Result; //- Join threads
result.ToList().ForEach(Console.WriteLine);
}
private int DoWork(int taskId)
{
return taskId;
}
Result output:
3
0
1
2
4

Categories