McSkelly
McSkelly

Reputation: 31

Cannot understand the for loops used in this code

I was trying out a solution to a question, when I came across this code snippet written in C++:

string s;
cin >> s;
vector<int> r;
for (string t: {"twone", "one", "two"}) {
    for (size_t pos = 0; (pos = s.find(t, pos)) != string::npos;) {
        s[pos + t.length() / 2] = '?';
        r.push_back(pos + t.length() / 2);
    }
}
cout << r.size() << endl;
for (auto rr: r)
    cout << rr + 1 << " ";
cout << endl;

I am new to the language and was unable to understand what is happening in the second (nested) for loop and the 3rd for loop. Can someone help me to understand?

Upvotes: 2

Views: 120

Answers (3)

Some programmer dude
Some programmer dude

Reputation: 409166

One of the main ways to try and understand complex code is to try and simplify it. It also helps to know what the involved functions do, so a reference to std::string::find is helpful to read.

First of all, lets skip the body and concentrate only on the loop itself:

for (size_t pos = 0; (pos = s.find(t, pos)) != string::npos;) {
}

All for loops could be seen as a while loop, and while loops could be somewhat easier to understand and follow, so we convert it to such a while loop:

size_t pos = 0;
while (pos = s.find(t, pos)) != string::npos)
{
}

This might not help so much as it's the condition that is most likely the hard part to understand, so then we simplify that as well:

size_t pos = 0;
pos = s.find(t, pos);
while (pos != string::npos)
{
    pos = s.find(t, pos);
}

The initialization of pos could then be further simplified:

size_t pos = s.find(t);
while (pos != string::npos)
{
    pos = s.find(t, pos);
}

Now the loop itself is a simple as it could be, and looking at it we see that basically attempt to find the sub-string t inside the string s. The loop continues as long as the sub-string t is found inside s.


Now that we deconstructed the loop itself, let's take a look at the loop-body, and what it does:

s[pos + t.length() / 2] = '?';
r.push_back(pos + t.length() / 2);

First of all lets pull out the common sub-expression into a temporary variable:

auto new_pos = pos + t.length() / 2;
s[new_pos] = '?';
r.push_back(new_pos);

The first statement

s[new_pos] = '?';

replaces the middle character of the sub-string t inside s with the character '?'.

The second statement

r.push_back(new_pos);

pushes the position of the '?' into the vector r.


Now lastly we put the inner loop (explained above) into the context of the outer loop:

for (string t: {"twone", "one", "two"})

This is a range-based for loop which loops over all elements in the container on the right-hand side of the :. That is, the loop will iterate three times, with t being equal to "twone", "one" and "two" in that order.

So loops will search for "twone", "one" and "two" inside the string s, replace the middle character of the sub-strings ("twone", "one" and "two") inside s with a single '?' character, and push the position of that '?' character into the vector r.

For example if the input in s is "someone with the number two" then the result will the the string "someo?e with the number t?o", and the vector r should contain the values 5 and 25 (which will be printed as 6 and 26 because of the + 1).

Here's an example shoing exactly that.

Upvotes: 1

Vlad from Moscow
Vlad from Moscow

Reputation: 310950

Just run the code inserting in it an output pf intermediate results.

Here is a demonstrative program.

#include <iostream>
#include <string>
#include <vector>

int main()
{
    std::string s;
    std::cin >> s;
    std::vector<int> r;

    for ( const std::string &t : { "twone", "one", "two" } ) 
    {
        for ( std::string::size_type pos = 0;  (pos = s.find( t, pos ) ) != std::string::npos; ) 
        {
            s[pos + t.length() / 2] = '?';
            std::cout << pos << ": " << s << '\n';
            r.push_back( pos + t.length() / 2 );
        }
    }

    std::cout << r.size() << '\n';

    for ( const auto &rr: r ) std::cout << rr + 1 << " ";
    std::cout << '\n';
}

Let's assume that the user entered string onetwoone. So the inner loop searches in the entered string all occurrences of words "twone", "one", "two" sequentially.

For the given string the word "twone" is not found.

The word "one" is found at position 0. This statement

s[pos + t.length() / 2] = '?';

the middle character of the found word in the entered string by the sign '?'.

Thus this added statement

std::cout << pos << ": " << s << '\n';

outputs

0: o?etwoone

The position of the sign '?' (the number 1) is stored in the vector.

Then within the loop the word "one" is found second time. And again the middle character of the found word is substituted for '?'. So this statement

std::cout << pos << ": " << s << '\n';

outputs

6: o?etwoo?e

The position of the sign '?' (the number 7) is stored in the vector.

So at this moment we have the following output

0: o?etwoone
6: o?etwoo?e

The word "one" is not found any more.

The word "two" is occurred only once in the given string. SO the output is

3: o?et?oo?e

The position of '?' equal to 4 is stored in the vector.

Now at this moment we have the following output

0: o?etwoone
6: o?etwoo?e
3: o?et?oo?e

produced by the inner loop.

So as a result three occurrences of the words are found in the entered string.

Thus these statements

std::cout << r.size() << '\n';

for ( const auto &rr: r ) std::cout << rr + 1 << " ";

output

3
2 8 5 

The last values correspond to expressions rr + 1 that is to stored positions of the sign '?' plus 1.

Upvotes: 1

Thomas Sablik
Thomas Sablik

Reputation: 16454

The first and the third loops are range-based for loops.

The first loop iterates over a container of strings. So t takes successively the value "twone", "one", and "two"

The second loop searches for all the occurences of t in the string s (each search starts from position pos of the previous occurence found). As long as a element is found it does:

s[pos + t.length() / 2] = '?';
r.push_back(pos + t.length() / 2);

The push_back() stores the position of the middle of each occurence found in a vector of integers.

The third loop iterates over this vector of stored positions and prints the elements (the positions count starts at 0, the +1 shifts the printed positions as if the count would start with 1).

Upvotes: 3

Related Questions