Reputation: 725
The program below is supposed to find the first all-zero row, if any, of an n × n matrix. Is this a correct approach to the problem, and is there another good structured approach to this code in C, or in any language in general? I'm trying to learn more about goto statements and appropriate uses/ alternatives to them. I appreciate any help!
int first_zero_row = -1; /* none */
int i, j;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
if (A[i][j]) goto next;
}
first_zero_row = i;
break;
next: ;
}
Upvotes: 4
Views: 4970
Reputation: 1
Since I've used my first goto to break out of nested loops in...well...ever, today, I'll weigh in on this. I felt bad about it so I read up a bit on why goto is considered evil.
The reason Dijkstra (and others) consider goto as evil is that code is built up in blocks where each block has preconditions and postconditions. A goto (or break) means that you cannot guarantee that a code block will generate a valid postcondition for the next block as precondition ( i.e. you cannot 'prove your code correct' since the break/goto may skip essential parts of the code block)
HOWEVER, the reason why I think goto is fine in your example is: You're actually setting up a fully valid postcondition (first_zero_row = -1 ...i.e.: "no all-zero row found") up front. So there is no chance that your block will go into an invalid postcondition state.
The issue of readability is another thing, though. Goto makes code rather unreadable. So for your particular example there may be better alternatives.
Upvotes: 0
Reputation: 10436
In my opinion, ALL GOTO
IS EVIL. If in any case you would need them, you'd better consider
"Extract Method" refactoring.
The need to use "GOTO" is a very good indicator shows your method (or function) is too complicated that you should split it into different pieces.
For example, the given example of the question can be (and should be) refactored into:
bool is_zero_row(int *row, int size){
for(i = 0; i<size; ++i){
if(row[i]) return false;
}
return true;
}
bool find_zero_row_in_array(int **A, int rows, int row_size){
int i,j;
for(i = 0; i < rows; ++i) {
if(is_zero_row(A[i], row_size)) return true;
}
return false;
}
...
if(find_zero_row_in_array(A, n, n)){
//Found
} else {
//Not found
}
....
This is far more flexible and readable than the original. At least we now have a function that can work for any arrays with different numbers of rows and columns, rather assume they are the same.
Upvotes: -2
Reputation: 9204
You can use one variable which will work as flag
. For if (A[i][j]) goto next;
condition modify flag value to some other instead of goto next
.
And then check that flag value later to do other operations , which is after next
label in your case.
int flag = 0;
for ( i = 0; i < n; i++ ) {
for ( j = 0; j < n; j++ ) {
if ( A[i][j] ){
flag = 1;
break;
}
}
if( flag == 1 ){
// do your stuff
continue;
}
if( flag == 0 ){
first_zero_row = i;
break;
}
NOTE : To remove goto
from your code It may require you to adjust the position of some lines of code.
Upvotes: 0
Reputation: 18960
Your code looks quite readable to me. Indiscriminate witch-hunting of goto
is not good. Go and read Donald E. Knuth’s Structured Programming with go to Statements for interesting thougts on the subject.
My version with goto
, for maximum efficiency and quite readable too:
int i, j;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++)
if (A[i][j]) goto not_this_row;
goto row_found;
not_this_row:
}
/* Not found case here */
row_found:
/* Found case, i is the first zero row. */
Upvotes: 1
Reputation: 92261
What about using a little helper function to simplify the code in the loop:
bool is_zero_row(int* Row, int size)
{
int j;
for (j = 0; j < size; j++)
if (Row[j] != 0)
return false;
return true;
}
and then
int first_zero_row = -1; /* none */
int i;
for (i = 0; i < n; i++) {
if (is_zero_row(A[i], n))
{
first_zero_row = i;
break;
}
}
Upvotes: 4
Reputation: 85767
First off: Yes, your approach looks correct to me. You could rewrite it like this:
int first_zero_row = -1; /* none */
int i, j;
for (i = 0; i < n; i++) {
int all_zeroes = 1;
for (j = 0; j < n; j++) {
if (A[i][j]) {
all_zeroes = 0;
break;
}
}
if (all_zeroes) {
first_zero_row = i;
break;
}
}
but I think that's actually less clear than the goto
version. In a language like Perl that provides loop labels, you could do this:
my $first_zero_row = -1; # none
ROW:
for my $i (0 .. $n-1) {
for my $j (0 .. $n-1) {
next ROW if $A[$i][$j];
}
$first_zero_row = $i;
last ROW;
}
Upvotes: 1
Reputation: 17312
I'm not against using goto
in some situations, but I think this can be re-written like this:
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
if (A[i][j]) break;
}
if (j==n) { /* the row was all zeros */
first_zero_row = i;
break;
}
}
Upvotes: 4