Simplifying Loops
and Logic
Simplifying Loops
and Logic
Trying minimize
the “mental baggage” of your code
Making Control Flow Easy to Read
Making Control Flow Easy to Read

KEY IDEA
Make all your conditionals, loops, and other changes to control flow as “natural”
as possible
KEY IDEA
Make all your conditionals, loops, and other changes to control flow as “natural” as possible
1. The Order of Arguments in Conditionals
1. The Order of Arguments in Conditionals
if (length > 10) Vs if (10 < length)
if (length > 10) Vs if (10 < length)
Left-hand side |
Right-hand side |
---|---|
The expression “being interrogated,” whose value is more in flux. |
The expression being compared against, whose value is more constant. |
2. The Order of if/else Blocks
2. The Order of if/else Blocks
if (a == b) {
// case one
} else {
// case two
}
if (a == b) {
// case one
} else {
// case two
}
if (a != b) {
// case two
} else {
// case one
}
if (a != b) {
// case two
} else {
// case one
}
Prefer dealing?
- positive case first instead of the negative
- e.g., if (debug) instead of if (!debug)
if (!url.HasQueryParameter("expand_all")) {
response.Render(items);
...
} else {
for (int i = 0; i < items.size(); i++) {
items[i].Expand();
}
}
if (!url.HasQueryParameter("expand_all")) {
response.Render(items);
...
} else {
for (int i = 0; i < items.size(); i++) {
items[i].Expand();
}
}
Don't think a pink elephant
but sometimes
negative case is the simpler and more interesting/dangerous one
if (not file) {
Log the error
} else {
// ...
}
3. Avoid do/while Loops
3. Avoid do/while Loops
do {
if(node.name().equals(name)) return true; node = node.next(); } while (node !== null && --max_length > 0)
while (node != null && max_length-- > 0) {
if (node.name().equals(name)) return true;
node = node.next();
}
Bjarne Stroustrup, the creator of C++, says it best
In my experience, the do-statement is a source of errors and confusion. ... I prefer the condition
“up front where I can see it.” Consequently, I tend to avoid do-statements.
Bjarne Stroustrup, the creator of C++, says it best
In my experience, the do-statement is a source of errors and confusion. ... I prefer the condition
“up front where I can see it.” Consequently, I tend to avoid do-statements.
4. Returning Early from a Function
4. Returning Early from a Function
function foo(str) {
if (typeof str !== 'string') return false;
body statment...
}
4. Minimize Nesting
4. Minimize Nesting
Each level of nesting pushes an extra condition onto
the reader’s “mental stack.”
Each level of nesting pushes an extra condition onto the reader’s “mental stack.”
if (user_result === SUCCESS) {
if (permission_result != SUCCESS) { reply.WriteErrors("error permission"); reply.Done(); return; } reply.WriteErrors(""); } else { reply.WriteErrors(user_result); } reply.Done();
Mental stack?
-
we have to keep values of
user_result
and
permission_result
in head at all times
- as each if { } block closes, you have to toggle the corresponding value in your mind
Key Idea
Look at your code from a fresh perspective when you’re making changes. Step back and look at it as a whole.
Removing Nesting by Returning Early
Removing Nesting by Returning Early
if (user_result != SUCCESS) { reply.WriteErrors(user_result); reply.Done(); return; } if (permission_result != SUCCESS) { reply.WriteErrors(permission_result); reply.Done(); return; }
reply.WriteErrors(""); reply.Done();
but sometimes it doesn't work...like below
but sometimes it doesn't work...like below
for (var i = 0; i < results.size(); i++) {
if (results[i] !== NULL) {
non_null_count++;
if (results[i].name !== "") {
console.log("Considering candidate...");
...
}
}
}
Solutions
Solutions
for (var i = 0; i < results.size(); i++) {
if (results[i] === NULL) continue;
non_null_count++;
if (results[i].name !== "") continue;
console.log("Considering candidate...");
...
}
Breaking Down Giant Expressions
Breaking Down Giant Expressions
Breaking Down Giant Expressions
KEY IDEA
Break down your giant expressions into more digestible pieces.
KEY IDEA
Break down your giant expressions into more digestible pieces.
so what's degestible pieces?
1.Explaining Variables
1.Explaining Variables
if (line.split(':')[0].strip() == 'root') {
...
}
if (line.split(':')[0].strip() == 'root') {
...
}
var username = line.split(':')[0].strip();
if (username == 'root') {
...
}
var username = line.split(':')[0].strip();
if (username == 'root') {
...
}
2.Summary Variables
2.Summary Variables
if (request.user.id === document.owner_id) { // can edit the doc }
...
if (request.user.id !== document.owner_id) { // can't ... }
if (request.user.id === document.owner_id) { // can edit the doc }
...
if (request.user.id !== document.owner_id) { // can't ... }
var canEditable = request.user.id === document.owner_id;
if (canEditable) { ... }
...
if (!canEditable) { ... }
var canEditable = request.user.id === document.owner_id;
if (canEditable) { ... }
...
if (!canEditable) { ... }
3.Morgan's Laws
3.Morgan's Laws
1) not (a or b or c) ⇔ (not a) and (not b) and (not c)
2) not (a and b and c) ⇔ (not a) or (not b) or (not c)
Factor out the 'not'! 1) not (a or b or c) ⇔ (not a) and (not b) and (not c)
2) not (a and b and c) ⇔ (not a) or (not b) or (not c)
if (!(file_exists && !is_protected)) Error("Sorry, could not read file.");
// after change
if (!file_exists || is_protected) Error("Sorry, could not read file.");
if (!(file_exists && !is_protected)) Error("Sorry, could not read file.");
// after change
if (!file_exists || is_protected) Error("Sorry, could not read file.");
4.Abusing short-circuit logic
4.Abusing short-circuit logic
assert((!(bucket = FindBucket(key))) || !bucket.IsOccupied());
Readable?If not, how?
assert((!(bucket = FindBucket(key))) || !bucket.IsOccupied());
var bucket = FindBucket(key);
if (bucket !== null) Assert(!bucket.IsOccpied());
var bucket = FindBucket(key);
if (bucket !== null) Assert(!bucket.IsOccpied());
Example: Wrestling with Complicated Logic
struct Range {
int begin;
int end;
// For example, [0, 5) overlaps with [3, 8)
bool OverlapsWith(Range other);
}
struct Range {
int begin;
int end;
// For example, [0, 5) overlaps with [3, 8)
bool OverlapsWith(Range other);
}

bool OverlapsWith(Range other) {
// Check if 'begin' or 'end' falls inside 'other'
return (begin >= other.begin && begin <= other.end) ||
(end >= other.begin && end <= other.end) ||
(begin <= other.begin && end >= other.end);
}
bool OverlapsWith(Range other) {
// Check if 'begin' or 'end' falls inside 'other'
return (begin >= other.begin && begin <= other.end) ||
(end >= other.begin && end <= other.end) ||
(begin <= other.begin && end >= other.end);
}
A more Elegant Approach!
A more Elegant Approach!
bool OverlapsWith(Range other) {
if (other.end <= begin) return false; // It end before we begin
if (other.begin >= end) return false; // It begin after we end
return true; // Only possibility left: they overlap
}
bool OverlapsWith(Range other) {
if (other.end <= begin) return false; // It end before we begin
if (other.begin >= end) return false; // It begin after we end
return true; // Only possibility left: they overlap
}
5.Breaking Down Giant Statements
5.Breaking Down Giant Statements
var updateHighlight = function (msg_num) {
if ($("#vote_value" + msg_num).html() === "UP") {
$("#thumbs_up" + message_num).addClass("highlighted");
$("#thumbs_down" + message_num).removeClass("highlighted");
} else if ($("#vote_value" + msg_num).html() === "DOWN") {
$("#thumbs_up" + message_num).removeClass("highlighted");
$("#thumbs_down" + message_num).addClass("highlighted");
} else {
$("#thumbs_up" + message_num).removeClass("highlighted");
$("#thumbs_down" + message_num).removeClass("highlighted");
}
}
var updateHighlight = function (msg_num) {
if ($("#vote_value" + msg_num).html() === "UP") {
$("#thumbs_up" + message_num).addClass("highlighted");
$("#thumbs_down" + message_num).removeClass("highlighted");
} else if ($("#vote_value" + msg_num).html() === "DOWN") {
$("#thumbs_up" + message_num).removeClass("highlighted");
$("#thumbs_down" + message_num).addClass("highlighted");
} else {
$("#thumbs_up" + message_num).removeClass("highlighted");
$("#thumbs_down" + message_num).removeClass("highlighted");
}
}
Don't Repeat Yourself
Don't Repeat Yourself
var updateHighlight = function (msg_num) {
var thumbs_up = $("#thumbs_up" + message_num);
var thumbs_down = $("#thumbs_down" + message_num);
var vote_value = $("#vote_value" + msg_num).html();
var hi = "highlighted"
if (vote_value === "UP") {
thumbs_up.addClass(hi);
thumbs_down.removeClass(hi);
} else if (vote_value === "DOWN") {
thumbs_up.removeClass(hi);
thumbs_down.addClass(hi);
} else {
thumbs_up.removeClass(hi);
thumbs_down.removeClass(hi);
}
}
var updateHighlight = function (msg_num) {
var thumbs_up = $("#thumbs_up" + message_num);
var thumbs_down = $("#thumbs_down" + message_num);
var vote_value = $("#vote_value" + msg_num).html();
var hi = "highlighted"
if (vote_value === "UP") {
thumbs_up.addClass(hi);
thumbs_down.removeClass(hi);
} else if (vote_value === "DOWN") {
thumbs_up.removeClass(hi);
thumbs_down.addClass(hi);
} else {
thumbs_up.removeClass(hi);
thumbs_down.removeClass(hi);
}
}
Variables and Readability
Variables and Readability
PROBLEMS:
HOW?
1. Eliminating Variables
2.Shrink the Scope of Your Varibles
1. Eliminating Variables
var now = datetime.now();
root_msg.last_view_time = now;
var now = datetime.now();
root_msg.last_view_time = now;
var root_msg.last_view_time = datetime.now();
var root_msg.last_view_time = datetime.now();

var remove_one = function (array, value_to_remove) {
var index_to_remove = null;
for (var i = 0; i < array.length; i += 1) {
if (array[i] === value_to_remove) {
index_to_remove = i;
break;
}
}
if (index_to_remove !== null) {
array.splice(index_to_remove, 1);
}
};
var remove_one = function (array, value_to_remove) {
var index_to_remove = null;
for (var i = 0; i < array.length; i += 1) {
if (array[i] === value_to_remove) {
index_to_remove = i;
break;
}
}
if (index_to_remove !== null) {
array.splice(index_to_remove, 1);
}
};
var remove_one = function (array, value_to_remove) {
for (var i = 0; i < array.length; i += 1) {
if (array[i] === value_to_remove) {
array.splice(i, 1);
return;
}
}
};
var remove_one = function (array, value_to_remove) {
for (var i = 0; i < array.length; i += 1) {
if (array[i] === value_to_remove) {
array.splice(i, 1);
return;
}
}
};
var done = false;
while (/* condition */ && !done) {
...
if (...) {
done = true;
continue;
}
}
var done = false;
while (/* condition */ && !done) {
...
if (...) {
done = true;
continue;
}
}
Variables like ‘done’ are
what we call “control flow variables.”
Their sole purpose is
to steer the program’s execution
while (/* condition */) {
...
if (...) {
break;
}
}
while (/* condition */) {
...
if (...) {
break;
}
}
2. Shrink the Scope of Your Variables
KEY IDEA
Make your variable visible by as few lines of code as possible.
KEY IDEA
Make your variable visible by as few lines of code as possible.
class LargeClass {
string str_;
void Method1() {
str_ = ...;
Method2();
}
void Method2() {
// Uses str_
}
// Lots methods that don't use str_ ...
};
class LargeClass {
string str_;
void Method1() {
str_ = ...;
Method2();
}
void Method2() {
// Uses str_
}
// Lots methods that don't use str_ ...
};
class LargeClass {
void Method1() {
str_ = ...;
Method2(str_);
}
void Method2() {
// Uses str_
}
// Now other methods can't see str_
};
class LargeClass {
void Method1() {
str_ = ...;
Method2(str_);
}
void Method2() {
// Uses str_
}
// Now other methods can't see str_
};
submitted = false; // Note: global variable
var submit_form = function (form_name) {
if (submitted) {
return; // don't double-submit the form
}
...
submitted = true;
};
submitted = false; // Note: global variable
var submit_form = function (form_name) {
if (submitted) {
return; // don't double-submit the form
}
...
submitted = true;
};
var submit_form = (function () {
submitted = false;
return function (form_name) {
if (submitted) {
return;
}
...
submitted = true;
}
}());
var submit_form = (function () {
submitted = false;
return function (form_name) {
if (submitted) {
return;
}
...
submitted = true;
}
}());
function test () {
var test_1,
test_2,
test_3,
test_4;
...
useTest_1(test_1);
}
function test () {
var test_1,
test_2,
test_3,
test_4;
...
useTest_1(test_1);
}
function test () {
var test_2,
test_3,
test_4;
...
var test_1;
useTest_1(test_1);
}
function test () {
var test_2,
test_3,
test_4;
...
var test_1;
useTest_1(test_1);
}
simplifying-loops-and-logic
By kangxiaojun
simplifying-loops-and-logic
share
- 886