Show Lecture.DivideAndConquer as a slide show.
CS253 Divide And Conquer
Julius Caesar, thinking “Veni, vidi, vici”
Horror Stories
- Don’t just start typing—you have to design your code.
- Programming tasks tend to be complex.
- Don’t do it all in main(), even if it works.
- If you fear writing functions, change your major now. 🍟
- If a program works, that’s just the start, not the end.
Maintainability matters.
- In the real world (and in CS253), programs live for a while.
Code from long ago will come back to haunt you.
- Also, modularity will enable you to actually write correct code.
Divide and Conquer
- The concept of “divide and conquer” comes from warfare.
- Don’t try to defeat all of your enemies at once.
- Divide your enemies into partisan groups that
won’t help each other.
- Then, defeat them separately, as smaller groups.
Programming
- In programming, we use similar divide and conquer techniques.
- Huge problems can be broken into several large problems.
- Large problems can be broken into several small problems.
- Solve each small problem separately.
- We have monkey brains that are not designed to create
complex algorithms. Make it easy on your 🐒 🧠 by
giving it smaller problems to deal with.
Programming
- Typically, when programming, we break down complex problems by
creating functions (or perhaps objects).
- Avoid the giant
MAIN()
FROM 🔥🔥🔥🔥.
- Instead, break down the problem into smaller problems,
one per function.
- If those functions are still too big, break them into
yet smaller functions.
- Rule of thumb: if a function doesn’t fit on the screen,
then it’s too big—break it up.
- However, don’t fear small functions: it’s fine to have a one-line
function
bool is_valid_input(char)
.
Example
Consider the program grep:
- It takes a regular expression pattern
and a number of filename arguments.
- It looks for the pattern in all the files (or standard input).
- It displays the lines that match the pattern.
- Depending on the options given:
- Display the lines that don’t match.
- Maybe the whole line, or maybe just the matching part.
- Maybe with line numbers.
- Maybe just the first matching line.
How not to do it
int main(int argc, char *argv[]) {
parse options;
if (no arguments)
for (each line in standard input)
if (line matches the pattern or maybe if it doesn’t) {
display the line number, if required;
display the line according to the options;
if (only list the first match)
break;
}
else
for (int i=1; i<argc; i++)
for (each line in that file)
if (line matches the pattern or maybe if it doesn’t) {
display the line number, if required;
display the line according to the options;
if (only list the first match)
break;
}
}
Not DRY, and would really be much longer. grep has over 40 options.
How to do it
int main(int argc, char *argv[]) {
parse_options(argv);
if (no arguments)
process_stream(cin);
else
for (int i=1; i<argc; i++)
process_file(argv[i]);
}
There—that’s manageable.
Break it down
void process_file(const string &filename) {
ifstream in(filename);
if (!in) {
complain;
exit(1);
}
process_stream(in);
}
No duplication of code: process_file()
uses process_stream()
.
Break it down
void process_stream(istream &in) {
for (string line; getline(in, line); )
if (match_pattern(line)) {
display_line(line);
if (only list the first match)
break;
}
}
- Each function does one easily-described task.
process_stream()
:
- doesn’t know anything about argv
- doesn’t care how
match_pattern()
works
- doesn’t care how
display_line()
works
- doesn’t even know what a regular expression is.
Objections
- This will make my program longer!
-
So? Are you running out of disk space?
- This will slow my program down!
-
Really? I’ll bet that you can’t measure the difference,
with all this file I/O and regular expression matching going on.
- I can understand it just fine if it’s all in main()! Can’t you ?
-
Aren’t you masculine. Yes, it’s possible to do that way, but, …,
why ? Save your effort for the tasks that need
to be difficult.