■ | Question SR1 |
What is your opinion regarding the "function should have only one exit point" rule? |
|
Answer SR1 |
By and large, I tend to agree with what Robert C. Martin ("Uncle Bob") said in his book ("Clean Code") regarding this controversial rule: |
QUOTE |
Some programmers follow Edsger Dijkstra's rules of structured programming. Dijkstra said that every function, and every block within a function, should have one entry and one exit. Following these rules means that there should only be one return statement in a function, no break or continue statements in a loop, and never, ever, any goto statements. While we are sympathetic to the goals and disciplines of structured programming, those rules serve little benefit when functions are very small. It is only in larger functions that such rules provide significant benefit. So if you keep your functions small, then the occasional multiple return, break, or continue statement does no harm and can sometimes even be more expressive than the single-entry, single-exit rule. On the other hand, goto only makes sense in large functions, so it should be avoided. |
END_QUOTE |
As an example, I'd typically write: |
bool allEven(const
int a[], int used)
{ for (int i = 0; i < used; ++i) if (a[i] % 2) return false; return true; } |
instead of (say) |
bool allEven(const
int a[], int used)
{ bool oddNotFound = true; for (int i = 0; i < used; ) if (a[i] % 2) { oddNotFound = false; i = used; } else ++i; return oddNotFound; } |
Here's a more involved example (from a past semester, involving linked list): |
(multi-exit version) |
bool LL2ContainsLL1(Node*
head1, Node* head2)
{ if (head1 == 0) return true; // empty list is always contained (even if 2nd list is empty) if (head2 == 0) return false; // list1 can't be empty so can't be contained if list2 is empty Node* copyOfHead2 = head2; while (head1 != 0) { while (copyOfHead2 != 0) { if (head1->data == copyOfHead2->data) break; copyOfHead2 = copyOfHead2->link; } if (copyOfHead2 == 0) return false; head1 = head1->link; copyOfHead2 = head2; } return true; } |
(single-exit version) |
bool LL2ContainsLL1(Node*
head1, Node* head2)
{ bool answer; if (head1 == 0) answer = true; // empty list is always contained (even if 2nd list is empty) else { if (head2 == 0) answer = false; // list1 can't be empty so can't be contained if list2 is empty else { Node* copyOfHead2 = head2; answer = true; while (head1 != 0 && answer) { bool matchFound = false; while (copyOfHead2 != 0 && !matchFound) if (head1->data == copyOfHead2->data) matchFound = true; else copyOfHead2 = copyOfHead2->link; if (!matchFound) answer = false; else { head1 = head1->link; copyOfHead2 = head2; } } } } return answer; } |