CS253: Software Development with C++

Spring 2019

DRY

Show Lecture.DRY as a slide show.

CS253 DRY

☔ ☔ ☔
Keep it DRY

Definition

D R Y

Don’t Repeat Yourself

Repetition is bad

Example

Here’s code to see if a number is present in a vector:

bool present(vector<int> vec, int target) {
    for (auto v : vec)
        if (v == target)
            return true;
    return false;
}

Apparently, the programmer didn’t know about set<int> or the find algorithm.

Example

We also need to find if a value is absent from a vector:

bool present(vector<int> vec, int target) {
    for (auto v : vec)
        if (v == target)
            return true;
    return false;
}

bool absent(vector<int> vec, int target) {
    for (auto v : vec)
        if (v != target)    // Changed == to !=
            return true;
    return false;
}

absent() is incorrect.

Try, try, again

Let’s try this:

bool present(vector<int> vec, int target) {
    for (auto v : vec)
        if (v == target)
            return true;
    return false;
}

bool absent(vector<int> vec, int target) {
    for (auto v : vec)
        if (v == target)
            return false;   // changed true to false
    return true;            // changed false to true
}

Better.

Not good enough

Sure, the previous solution is correct, but it’s not maintainable. What if we have to modify present(), say, to treat negative values as equal to positive values? Will we remember to also modify absent()?

bool present(vector<int> vec, int target) {
    for (auto v : vec)
        if (v == target)
            return true;
    return false;
}

bool absent(vector<int> vec, int target) {
    return !present(vec, target));
}

Correct, and DRY.

Using references

As we all know, a reference creates an alias for another thing. For example:

int a = 42;
int &r = a;
cout << "r=" << r << " a=" << a << '\n';
a = 99;
cout << "r=" << r << " a=" << a << '\n';
r = 56;
cout << "r=" << r << " a=" << a << '\n';
r=42 a=42
r=99 a=99
r=56 a=56

Loops

Let’s say that we want to change all the fives in this vector to negative fives:

vector<int> values = {3,1,4,1,5,9,2,6,5,3,5};
for (size_t i=0; i<values.size(); i++)
    if (values[i] == 5)
        values[i] = -5;
for (size_t i=0; i<values.size(); i++)
    cout << values[i] << ' ';
3 1 4 1 -5 9 2 6 -5 3 -5 

That works, but it’s a lot of repetition—quite a few instances of values[i].

Better loops

Let’s use a more modern loop to write out the numbers:

vector<int> values = {3,1,4,1,5,9,2,6,5,3,5};
for (size_t i=0; i<values.size(); i++)
    if (values[i] == 5)
        values[i] = -5;
for (int v : values)
    cout << v << ' ';
3 1 4 1 -5 9 2 6 -5 3 -5 

Hooray! One fewer values[i]!

Betterer loops

Let’s use that same technique to change 5 to −5:

vector<int> values = {3,1,4,1,5,9,2,6,5,3,5};
for (int v : values)
    if (v == 5)
        v = -5;
for (int v : values)
    cout << v << ' ';
3 1 4 1 5 9 2 6 5 3 5 

That didn’t work. Why?

Best loops

REFERENCES!

vector<int> values = {3,1,4,1,5,9,2,6,5,3,5};
for (int &v : values)
    if (v == 5)
        v = -5;
for (int v : values)
    cout << v << ' ';
3 1 4 1 -5 9 2 6 -5 3 -5 

I need &v in the first loop, because I’m changing the number. I’m not changing anything in the second loop, so a reference isn’t required.

C🐵ns🐵rsh🐵p

Ducks

Consider this file:

% cat ~cs253/pub/ducks
Huey (red)
Dewey (blue)
Louie (green)

I want code that reads that file into a vector<string>, and converts the lower-case vowels to asterisks, so it looks like censorship.

Attempt #1

const string home = getpwnam("cs253")->pw_dir;
ifstream in(home+"/pub/ducks");
vector<string> ducks;
string line;
while (getline(in, line))
    ducks.push_back(line);
for (size_t i=0; i<ducks.size(); i++)
    for (size_t j=0; j<ducks[i].size(); j++)
        if (ducks[i][j]=='a' || ducks[i][j]=='e' ||
            ducks[i][j]=='i' || ducks[i][j]=='o' ||
            ducks[i][j]=='u')
            ducks[i][j] = '*';
for (size_t i=0; i<ducks.size(); i++)
    cout << ducks[i] << '\n';
H**y (r*d)
D*w*y (bl**)
L**** (gr**n)

That was horrible. So soon, we’ve forgotten all that we’ve learned.

Attempt #2

const string home = getpwnam("cs253")->pw_dir;
ifstream in(home+"/pub/ducks");
vector<string> ducks;
string line;
while (getline(in, line))
    ducks.push_back(line);
for (string &s : ducks)
    for (size_t j=0; j<s.size(); j++)
        if (s[j]=='a' || s[j]=='e' ||
            s[j]=='i' || s[j]=='o' ||
            s[j]=='u')
            s[j] = '*';
for (string s : ducks)
    cout << s << '\n';
H**y (r*d)
D*w*y (bl**)
L**** (gr**n)

Better, but still awful.

Attempt #3

const string home = getpwnam("cs253")->pw_dir;
ifstream in(home+"/pub/ducks");
vector<string> ducks;
string line;
while (getline(in, line))
    ducks.push_back(line);
for (string &s : ducks)
    for (char &c : s)
        if (c=='a' || c=='e' || c=='i' || c=='o' || c=='u')
            c = '*';
for (string s : ducks)
    cout << s << '\n';
H**y (r*d)
D*w*y (bl**)
L**** (gr**n)

Finally, it’s DRY! Still, we’re copying strings in the last loop.

Attempt #4

const string home = getpwnam("cs253")->pw_dir;
ifstream in(home+"/pub/ducks");
vector<string> ducks;
string line;
while (getline(in, line))
    ducks.push_back(line);
for (string &s : ducks)
    for (char &c : s)
        if (c=='a' || c=='e' || c=='i' || c=='o' || c=='u')
            c = '*';
for (const string &s : ducks)
    cout << s << '\n';
H**y (r*d)
D*w*y (bl**)
L**** (gr**n)

DRY and efficient.

Attempt #5

const string home = getpwnam("cs253")->pw_dir;
ifstream in(home+"/pub/ducks");
vector<string> ducks;
string line;
while (getline(in, line))
    ducks.push_back(line);
for (string &s : ducks)
    for (size_t p=0; (p = s.find_first_of("aeiou",p)) != s.npos; p++)
        s[p] = '*';
for (const string &s : ducks)
    cout << s << '\n';
H**y (r*d)
D*w*y (bl**)
L**** (gr**n)

A different approach.

User: Guest

Check: HTML CSS
Edit History Source

Modified: 2019-04-27T15:58

Apply to CSU | Contact CSU | Disclaimer | Equal Opportunity
Colorado State University, Fort Collins, CO 80523 USA
© 2018 Colorado State University
CS Building