What does a single semicolon in a for loop mean? - c#

I was following a tutorial for how to make an insertion sort and came across this line of code:
for(; j >= 0 && tempVar < insertionArray[j]; j--)
My question is, what does the single semicolon mean after the first bracket?
I know that a double semicolon in a for loop defines an infinite loop,
for(;;)
but what does a single one mean?

There are 3 parts to a for loop, each separated by semicolons:
initialisation; evaluation ; iteration
for ( int i = 0 ; i < someValue ; i++ )
If you have already initialized the variable that you want to evaluate and iterate through, you don't need to do it in the loop:
int i = 0;
for(; i < someValue ; i++ )

The first part of a for loop is to declare and initialize the counter variable (j in this case). Since it was declared and initialized before the loop, we don't do it again.

This just the initializer section
The statements in the initializer section are executed only once, before entering the loop. The initializer section is either of the following:
The declaration and initialization of a local loop variable, which can't be accessed from outside the loop.
Zero or more statement expressions from the following list, separated by commas:
assignment statement
invocation of a method
prefix or postfix increment expression, such as ++i or i++
prefix or postfix decrement expression, such as --i or i--
creation of an object by using new keyword
await expression

It divides the line into different blocks.
The first part is one time statement, the second is the condition of the loop and the third is a operation to do after/before the loop's code.

Related

IEnumerable IndexOutOfRangeException

I dont know why this I'm getting System.IndexOutOfRangeException: 'Index was outside the bounds of the array.' with this code
IEnumerable<char> query = "Text result";
string illegals = "abcet";
for (int i = 0; i < illegals.Length; i++)
{
query = query.Where(c => c != illegals[i]);
}
foreach (var item in query)
{
Console.Write(item);
}
Please can someone explain what's wrong with my code.
The problem is that your lambda expression is capturing the variable i, but the delegate isn't being executed until after the loop. By the time the expression c != illegals[i] is executed, i is illegals.Length, because that's the final value of i. It's important to understand that lambda expressions capture variables, rather than "the values of those variables at the point of the lambda expression being converted into a delegate".
Here are five ways of fixing your code:
Option 1: local copy of i
Copy the value of i into a local variable within the loop, so that each iteration of the loop captures a new variable in the lambda expression. That new variable isn't changed by the rest of the execution of the loop.
for (int i = 0; i < illegals.Length; i++)
{
int copy = i;
query = query.Where(c => c != illegals[copy]);
}
Option 2: extract illegals[i] outside the lambda expression
Extract the value of illegals[i] in the loop (outside the lambda expression) and use that value in the lambda expression. Again, the changing value of i doesn't affect the variable.
for (int i = 0; i < illegals.Length; i++)
{
char illegal = illegals[i];
query = query.Where(c => c != illegal);
}
Option 3: use a foreach loop
This option only works properly with C# 5 and later compilers, as the meaning of foreach changed (for the better) in C# 5.
foreach (char illegal in illegals)
{
query = query.Where(c => c != illegal);
}
Option 4: use Except once
LINQ provides a method to perform set exclusion: Except. This is not quite the same as the earlier options though, as you'll only get a single copy of any particular character in your output. So if e wasn't in illegals, you'd get a result of "Tex resul" with the above options, but "Tex rsul" using Except. Still, it's worth knowing about:
// Replace the loop entirely with this
query = query.Except(illegals);
Option 5: Use Contains once
You can call Where once, with a lambda expression that calls Contains:
// Replace the loop entirely with this
query = query.Where(c => !illegals.Contains(c));
This happens because, although your for loop seems at first glance to be correctly bounded, each iteration captures the index in the closure that is passed to Where. one of the most useful properties of closures is that they capture by reference, enabling all sorts of powerful and sophisticated techniques. However, in this case it means that, by the time the query is executed in the ensuing foreach loop. The index has been incremented past the length of the array.
The most straightforward change to fix this is create a loop scoped copy the current value of the index loop control variable and refer to this in your closure instead of referring directly to the loop control variable.
Ex:
for (int i = 0; i < illegals.Length; i++)
{
var index = i;
query = query.Where(c => c != illegals[index]);
}
However, as has been noted by others, there are better ways to write this that void the problem entirely and they also have the virtue that they raise the level of abstraction.
For example, you can use System.Linq.Enumerable.Except
var legals = query.Except(illegals);

cannot understand lambda expression output [duplicate]

This question already has answers here:
Captured variable in a loop in C#
(10 answers)
Closed last month.
I have this code and do not understand why the out put is 22! I am afraid it should be 01!
can anyone explain what happens? if the list store a method with a parameter, so the parameters should be 0 and 1 respectively!
List<Action> list = new List<Action>();
for (int i = 0; i < 2; i++)
{
list.Add(() => Console.Write(i));
}
foreach (var it in list)
{
it();
}
It is Closure (1, 2).
In your case Console.Write(i) will use value of i in the moment of action call. You firstly increment i in for loop then in second loop you call every action in the list. In the moment of call of every action i has value 2 - so, you get 22 as output.
To get expected result you should create local copy of i and use it:
for (int i = 0; i < 2; i++)
{
var temp = i;
list.Add(() => Console.Write(temp));
}
Addition to Roma Doskoch's anwser, another approach is to avoid for.
var list = Enumerable
.Range(0, 2)
.Select<int, Action>(i => () => Console.Write(i));
Closures capture variables, not values.
In your code, the closure captures the variable i, not whatever value happens to be stored in i on each iteration. When you invoke the action, the variable i has a value of 2 (because the loop has finished) and therefore 2 will be printed out twice.
In order to avoid this, as other answers already point out, you need to create a new variable every time around as a workaround to not being able to capture values; if you declare a new variable on every iteration then the result of capturing the variable is equivalent to capturing the value because you won't be changing it on the next iteration.

Using random rnd inside a method causes multiple errors

Having problem with the method down below. The current error I get is "it does not return any value" If I put the return num outside of the brackets, it does not exist in the current context.
If I remove the brackets all together I get:
Embedded statement cannot be a declaration or labeled statement
The name 'rnd' does not exist in the current context
I've done multiple methods before without this issue. What am I missing?
public static int generateNumber(int timesUserWantsToGuess)
{
for (int i = 0; i <= timesUserWantsToGuess; i++)
{
Random rnd = new Random();
int num = rnd.Next(1, 50);
return num;
}
}
First off you have a mismatch of semantics. You are returning a single int, but are executing and returning within a loop.
If you actually wanted to return a collection of ints, you could do something like this:
public static IEnumerable<int> generateNumber(int timesUserWantsToGuess)
{
//Optional, but good error checking
if (timesUserWantsToGuess <= 0)
throw new ArgumentException("Invalid guess count");
Random rnd = new Random();
for (int i = 0; i < timesUserWantsToGuess; i++)
{
int num = rnd.Next(1, 50);
yield return num;
}
}
Note the use of yield to return the next object in the sequence. You also shouldn't instantiate Random in a tight loop, since it will cause the same values to be generated (same seed). Also, using <= in the for conditional will yield one more iteration than the passed value, so I switched to <.
If you didn't want a loop at all, then just remove it and your problem will be solved. At the moment, the loop semantic and the rest of your code mismatch, which is causing you the problem.
The issue is that since the return statement exists inside a loop, the compiler assumes there's a chance it could execute without ever hitting the block, and thus won't return. This could logically happen if timesUserWantsToGuess is less than 0 - the loop never enters in this case. The reason this code compiles if the return statement is outside the block is that by being outside the block, the execution guarantees it'll hit the return (or throw an exception) at some point.
If you remove the brackets on a block in C# (if, for, foreach, lock, and while all do this to my knowledge) you are saying that the block contains only the next line (which is indented by convention) and nothing more. Since variable declaration on a one-line ("embedded") statement can't be used anywhere else syntactically, it doesn't make sense to do it, so the compiler forbids it altogether. Be noted it is also possible to declare a variable in a block without using it, as long as the block is bracketed.
Finally, there's a huge semantic mismatch in the logic of returning a single value during a loop like this: see #BradleyDotNET's answer on yield return statements

CLR is optimising my forloop variables away

I'm trying to run a basic loop that will find a specific value in a dataview grid. I cannot figure out whats going on with the code, since the for loop exits before evaluating its basic condition.
private void SearchDataViewGrid(string FileName)
{
//finds the selected entry in the DVG based on the image
for (int i = 0; i == dataPartsList.Rows.Count ; i++)
{
if(FileName == dataPartsList.Rows[i].Cells[3].Value.ToString())
{
dataPartsList.Rows[i].Selected = true;
}
}
}
The program doesn't crash, but i get an error on my 'i' variables declaring that it has been optimised away. Tried a few easy fixes i found online but nothing seems to keep it.
I have verified that the string i am passing is the correct one, and my 'dummy' DVG returns a value of 14 for the number of rows contained. Even if i remove the 'if' statement inside of the for loop, i still get the same error.
The condition cond in the middle of for(init; cond; update) is not an until condition but a while condition.
So you need to change it to
for (int i = 0; i < dataPartsList.Rows.Count ; i++)
The problem is your conditional is i == dataPartsList.Rows.Count so the body will only execute when these two values are equal. This guarantees your loop will never execute. You need to change your conditional to be < instead of ==
for (int i = 0; i < dataPartsList.Rows.Count ; i++) {
...
}

Is the condition in a for loop evaluated each iteration?

When you do stuff like:
for (int i = 0; i < collection.Count; ++i )
is collection.Count called on every iteration?
Would the result change if the Count property dynamically gets the count on call?
Yes Count will be evaluated on every single pass. The reason why is that it's possible for the collection to be modified during the execution of a loop. Given the loop structure the variable i should represent a valid index into the collection during an iteration. If the check was not done on every loop then this is not provably true. Example case
for ( int i = 0; i < collection.Count; i++ ) {
collection.Clear();
}
The one exception to this rule is looping over an array where the constraint is the Length.
for ( int i = 0; i < someArray.Length; i++ ) {
// Code
}
The CLR JIT will special case this type of loop, in certain circumstances, since the length of an array can't change. In those cases, bounds checking will only occur once.
Reference: http://blogs.msdn.com/brada/archive/2005/04/23/411321.aspx
Count would be evaluated on every pass. If you continued to add to the collection and the iterator never caught up, you would have an endless loop.
class Program
{
static void Main(string[] args)
{
List<int> intCollection = new List<int>();
for(int i=-1;i < intCollection.Count;i++)
{
intCollection.Add(i + 1);
}
}
}
This eventually will get an out of memory exception.
Yes count is checked at every call from the first iteration after the initialization of i to the last iteration where the check fails and the for loop is exited. You can modify the collections count if you want but realize you could end up in an endless loop.
Like the other answers here: Yes, in principal.
There is (at least) one noticeable exception, array.Length. In
for (int i = 0; i < a.Length; i++) a[i] = ...;
The Length property will only be evaluated once. This is a optimization that is hardwired into the compiler. There might be others like that (in the future) but only if it is guaranteed not to make a difference in observable behavior.
Side note, this is NOT checked for every interation in VB.
Unlike C#, VB caches the result of the collection.Count.
EDIT:
The literal VB version of the C# for loop is:
Dim i = 0
Do While i < collection.Count
'code goes here
i+=1
Loop

Categories

Resources