This question already has answers here:
Break out of a while loop that contains a switch statement
(15 answers)
Closed 8 years ago.
I was asked to do maintenance work on some C# code which, I'm told, was originally converted from Visual Basic 6. (I mention this only because I don't know VB6 so I don't know if it would have made more sense in that language. . .)
It's got a for loop which parses some text for a proprietary scripting language by using a switch inside the for loop . . .
for ( t = 0; t < upperBound(tokens); t++)
{
String mystring = tokens[t];
switch (mystring)
{
case "GOTO":
if (firstGoto == -1)
{
firstGoto = t;
}
else
{
// compute number of tokens in GOTO
pointLength = t - firstGoto - 1;
break; // exit for
}
break;
case "ACTUATE"
. . .
Notice the comment
// exit for
The programmer expects the break will exit the for loop but I think it will only exit the switch statement because the documentation for break says
The break statement terminates the closest enclosing loop or switch statement in which it appears. Control is passed to the statement that follows the terminated statement, if any.
So am I correct that this will only exit the switch, but still be in the for, and if so, what is the correct way to do what the original programmer intended?
Yes, break will break out of the closest loop or switch. The easiest way is to use a goto. (No, goto is not evil)
for {
switch(...) {
....
goto MyLabel;
}
}
MyLabel:
Yes, you are correct... break will only exit the innermost block of code.
One easy way to do this is to also have a boolean flag which is set within the switch statement, and at the end of the loop just do this:
var flag = false;
for (...) {
switch (...) {
case "x":
flag = true;
}
if (flag) break;
}
Yes break will only terminate the switch in this scenario. How about using a boolean to exit the for as well..?
for ( t = 0; t < upperBound(tokens); t++)
{
String mystring = tokens[t];
bool exitFor = false;
switch (mystring)
{
case "GOTO":
if (firstGoto == -1)
{
firstGoto = t;
}
else
{
// compute number of tokens in GOTO
pointLength = t - firstGoto - 1;
exitFor = true;
break; // exit for
}
break;
case "ACTUATE"
. . .
if(exitFor)
break;
Related
Im looping a question untill the user puts in the value i want to accept. In this case its a number with a dash and it should be 7 'symbols' long.
My problem is putting the ReadLine inside of the while() statement.
So this is my code:
string cpr = "";
do
{
cpr = Console.ReadLine(); //I dont want ReadLine here :/
} while (
//I want Console.ReadLine() here
cpr.Length != 7
&&
!Regex.IsMatch(cpr, #"^[0-9-]+$")
&&
Regex.IsMatch(cpr, #"^[a-z]+$")
);
Putting ReadLine in a while statement is possible i have some other code that works while try parsing an int
do
{
//do something here
} while (!int.TryParse(Console.ReadLine(), out int1));
Try something like this:
string cpr = "";
bool condition = true;
while (condition )
{
cpr = Console.ReadLine();
if(cpr is ok)
{
condition = false;
}
}
While I agree with the comments (in that this isn't the best way to approach this - in fact, the code you've provided is more than adequate), you can do assignment inside of a conditional statement. ie.
var str = "";
do
{
...
} while ((str = Console.ReadLine()).Length != 7 && Regex.IsMatch(str, ...));
Except in this case, you'd need to use a while loop, instead of a do-while (since you won't capture input until the end of the loop with the do-while pattern).
Let's say I need to go through bunch of statements and process each case, results of which will be "incrementation" of IntVar. So like:
int result = 1;
if (condition1)
{
result = result + 2;
}
if (condition2)
{
result++;
}
if (condition3)
{
result++;
}
//things get hairy
if (result != 5 && condition4)
{
result++;
}
if (result != 5 && condition5)
{
result++;
}
// do a thing based on a result value
I' love to use while{} here but I can't because result can be less than 5 when all cases processed. What i had in mind is putting all the checks into SWITCH and make continue to process them until the end (and then continue code normally) UNLESS result hits "5" middle way, then just go straight to result process.
Is it possible to force default SWITCH to do so without writing custom one and/or overcomplicating things too much?
I am making an OS with Cosmos and want to use goto to go to the user input but I am getting the error
No such label 'input' within the scope of the goto statement
'input' is a variable in which the user has inputted.
I can understand why this is happening but how do I fix it?
You cannot user variables as scope identifier for goto statement.. you have to use label identifier within scope (namespace) indicating it by ":" ..
for example
using System;
class Program
{
static void Main()
{
Console.WriteLine(M());
}
static int M()
{
int dummy = 0;
for (int a = 0; a < 10; a++)
{
for (int y = 0; y < 10; y++) // Run until condition.
{
for (int x = 0; x < 10; x++) // Run until condition.
{
if (x == 5 &&
y == 5)
{
goto Outer;
}
}
dummy++;
}
Outer:
continue;
}
return dummy;
}
}
method M contains three nested loops. The first loop iterates through numbers [0, 9], as do the two inner loops. But in the third loop, a condition is checked that causes the loop to exit using the break keyword.
For
Break
The code increments the dummy variable after each completion of the inner loop. If the inner loop is exited early, this variable should be left alone. With the goto statement, it is not incremented.
Result:
The value 50 is printed to the console. The int is incremented 10 x 5 times.
However:
If the goto was a break, the result would be 10 x 10 times, or a total of 100.
Hope this Help.. :)
I am making an OS with Cosmos
For getting any remotely useful answers, I think you will have to give some information about the scope of the OS. Are you only fiddling around with COSMOS a bit, or do you have some special use-case you want to serve with a custom COSMOS OS?
and want to use goto to go to the user input
Especially in the latter case (specialized OS) you should clearly refrain from using GOTO, unless you have a very good reason to do so (and in my humble opinion there is no such thing as a really good reason to use GOTO). There are viable alternatives to GOTOs in modern programming languages and you should re-think your design, algorithm, whatsoever.
To answer your question. Here is an example that produces the very error message you are experiencing
private void FirstMethod()
{
goto MyLabel;
}
private void SecondMethod()
{
MyLabel:
return;
}
I have defined a label in Method. Anyway, from Main you cannot simply jump from main to another method, since the compiler would not know where to return to, after the method has finished, since no data would have been pushed to the call stack on GOTO (please see the Wikipedia page about the call stack for further information).
The following, anyway, would work, since the label and the GOTO live within the same scope
void MyMethod()
{
goto MyLabel;
// do something
MyLabel:
return;
}
I want to read a file containing comma-separated values, so have written a finite state machine:
private IList<string> Split(string line)
{
List<string> values = new List<string>();
string value = string.Empty;
ParseState state = ParseState.Initial;
foreach (char c in line)
{
switch (state)
{
case ParseState.Initial:
switch (c)
{
case COMMA:
values.Add(string.Empty);
break;
case QUOTE:
state = ParseState.Quote;
break;
default:
value += c;
state = ParseState.Data;
break;
}
break;
case ParseState.Data:
switch (c)
{
case COMMA:
values.Add(value);
value = string.Empty;
state = ParseState.Initial;
break;
case QUOTE:
throw new InvalidDataException("Improper quotes");
default:
value += c;
break;
}
break;
case ParseState.Quote:
switch (c)
{
case QUOTE:
state = ParseState.QuoteInQuote;
break;
default:
value += c;
break;
}
break;
case ParseState.QuoteInQuote:
switch (c)
{
case COMMA:
values.Add(value);
value = string.Empty;
state = ParseState.Initial;
break;
case QUOTE:
value += c;
state = ParseState.Quote;
break;
default:
throw new InvalidDataException("Unpaired quotes");
}
break;
}
}
switch (state)
{
case ParseState.Initial:
case ParseState.Data:
case ParseState.QuoteInQuote:
values.Add(value);
break;
case ParseState.Quote:
throw new InvalidDataException("Unclosed quotes");
}
return values;
}
Yes, I know the advice about CSV parsers is "don't write your own", but
I needed it quickly and
our download policy at work would take several days to allow me to
get open source off the 'net.
Hey, at least I didn't start with string.Split() or, worse, try using a Regex!
And yes, I know it could be improved by using a StringBuilder, and it's restrictive on quotes in the data, but
performance is not an issue and
this is only to generate well-defined test data in-house,
so I don't care about those.
What I do care about is the apparent trailing block at the end for mopping up all the data after the final comma, and the way that it's starting to look like some sort of an anti-pattern down there, which was exactly the sort of thing that "good" patterns such as a FSM were supposed to avoid.
So my question is this: is this block at the end some sort of anti-pattern, and is it something that's going to come back to bite me in the future?
All of the FSMs I've ever seen (not that I go hunting for them, mind you) all have some kind of "mopping up" step, simply due to the nature of enumeration.
In an FSM you're always acting upon the current state, and then resetting the 'current state' for the next iteration, so once you've hit the end of your iterations you have to do one last operation to act upon the 'current state'. (Might be better to think about it as acting upon the 'previous state' and setting the 'current state').
Therefore, I would consider that what you've done is part of the pattern.
But why didn't you try some of the other answers on SO?
Split CSV String (specifically this answer)
How to properly split a CSV using C# split() function? (specifically this answer)
Adapted solution, still an FSM:
public IEnumerable<string> fsm(string s)
{
int i, a = 0, l = s.Length;
var q = true;
for (i = 0; i < l; i++)
{
switch (s[i])
{
case ',':
if (q)
{
yield return s.Substring(a, i - a).Trim();
a = i + 1;
}
break;
// pick your flavor
case '"':
//case '\'':
q = !q;
break;
}
}
yield return s.Substring(a).Trim();
}
// === usage ===
var row = fsm(csvLine).ToList();
foreach(var column in fsm(csvLine)) { ... }
In a FSM you identify which states are the permitted halting states. So in a typical implementation, when you come out of the loop you need to at least check to make sure that your last state is one of the permitting halting states or throw a jam error. So having that one last state check outside of the loop is part of the pattern.
The source of the problem, if you want to call it that, is the absence of an end-of-line marker in your input data. Add a newline character, for example, at the end of your input string and you will be able to get rid of the "trailing block" that seems to annoy you so much.
As far as I'm concerned, your code is correct and, no, there is no reason why this implementation will come back to bite you in the future!
I had a similiar issue, but i was parsing a text file character by character. I didnt like this big clean-up-switch-block after the while loop. To solve this, I made a wrapper for the streamreader. The wrapper checked when streamreader had no characters left. In this case, the wrapper would return an EOT-ascii character once (EOT is equal to EOF). This way my state machine could react to the EOF depending on the state it was in at that moment.
Consider the following C#:
// C# .net
switch(x)
{
case 1:
for(int i = 0; i < 10; i++)
{
int val = getValue(i);
if (val == 0)
goto endswitch;
}
doMoreStuff();
break;
case 2:
doSomeThingElse();
break;
default: throw new ArgumentOutOfRangeException();
}
endswitch: ;
I've written code similar to the above code sample. The problem is that I need to break the switch statement from inside the inner for loop. If I put a break statement there, it will only break the inner for loop and then proceed to doMoreStuff(), which is not what I need.
The alternative that seems to work best here is a goto statement, but I know this is frowned upon.
Another alternative is to keep track of a separate variable inside the for loop, but this adds lines of code and is less elegent.
What is the best way to do this?
Update: I have read that there is a way to do this in JavaScript. It works like this: (http://www.devguru.com/technologies/ecmascript/quickref/break.html)
// JavaScript
outer_loop:
for(i=0; i<3; i++)
{
document.write("<BR>" + "outer " + i + ": ");
for(j=0; j<5; j++)
{
document.write("inner " + j + " ");
if(j==x)
break outer_loop;
}
}
Is something like this possible in C#?
You can abstract the check to a method with a return flag:
switch(x)
{
case 1:
if (ShouldDoMoreStuff())
doMoreStuff();
break;
case 2:
doSomeThingElse();
break;
default: throw new ArgumentOutOfRangeException();
}
private bool ShouldDoMoreStuff()
{
for(int i = 0; i < 10; i++)
{
var val = getValue(i);
if (val == 0)
return false;
}
return true;
}
Just to expand, you can use the goto as you had, but it's generally frowned upon, especially in such a trivial case. Sometimes it's useful when you have many, many nested loops or switches but that's usually a sign that maybe you should refactor/redesign a bit. As you pointed out, you can store a local variable and do a check but that's a bit obtuse/smelly as you realized. I prefer this method I posted above as it becomes pretty readable to work with.
EDIT: Regarding your comment and edited question, I do not believe a analogous language feature to your JavaScript code exists in C#. From http://msdn.microsoft.com/en-us/library/37zc9d2w%28VS.80%29.aspx (emphasis added):
Within nested statements, the break statement terminates only the do,
for, switch, or while statement that immediately encloses it. You can
use a return or goto statement to transfer control from within more
deeply nested structures.
This is the type of problem caused by too much thinking. You could simply have a boolean that will be altered if your condition is met before the for loop and run your method only if the boolean is true, e.g.:
switch(x)
{
case 1:
bool donrun;
for(int i = 0; i < 10; i++)
{
int val = getValue(i);
if (val == 0)
donrun = true;
}
if(!donrun) doMoreStuff();
break;
case 2:
doSomeThingElse();
break;
default: throw new ArgumentOutOfRangeException();
}