Не могу понять циклы for, используемые в этом коде - PullRequest
2 голосов
/ 19 января 2020

Я пытался найти решение вопроса, когда наткнулся на этот фрагмент кода, написанный на 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;

Я новичок в языке и не смог понять, что происходит во втором (вложенный) для l oop и третий для l oop. Может ли кто-нибудь помочь мне понять?

Ответы [ 3 ]

3 голосов
/ 19 января 2020

Первый и третий циклы основаны на диапазоне для циклов .

Первый l oop перебирает контейнер строк. Таким образом, t принимает последовательно значения "twone", "one" и "two"

Второй l oop ищет все вхождения t в строке s (каждый поиск начинается с позиции pos предыдущего найденного вхождения). Пока элемент найден, он делает:

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

push_back() хранит положение середины каждого вхождения, найденного в векторе целых чисел.

Третий l oop перебирает этот вектор сохраненных позиций и печатает элементы (счетчик позиций начинается с 0, +1 смещает напечатанные позиции, как если бы счет начинался с 1).

1 голос
/ 19 января 2020

Один из основных способов понять сложный код - попытаться упростить его. Это также помогает узнать, что делают задействованные функции, поэтому полезно прочитать ссылку на std::string::find.

Прежде всего, давайте пропустим тело и сконцентрируемся только на l * Сама 1091 *:

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

Все циклы for можно рассматривать как while l oop, а циклы while могут быть несколько проще для понимания и отслеживания, поэтому мы конвертируем их в такой while l oop:

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

Это может не сильно помочь, так как это условие, которое, скорее всего, трудно понять, поэтому мы упростим и это:

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

Инициализация pos может быть еще более упрощена:

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

Теперь сам l oop настолько прост, насколько это возможно, и, глядя на него, мы видим, что в основном попытаться найти подстроку t внутри строки s. L oop продолжается до тех пор, пока подстрока t находится внутри s.


Теперь, когда мы деконструировали сам l oop, давайте посмотрим на l oop -тела и что он делает:

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

Прежде всего давайте вытянем общее подвыражение во временную переменную:

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

Первый оператор

s[new_pos] = '?';

заменяет средний символ подстроки t внутри s на символ '?'.

Второй оператор

r.push_back(new_pos);

толкает положение '?' в векторе r.


Теперь, наконец, мы помещаем внутренний l oop (объясненный выше) в контекст внешнего l oop:

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

Это основанный на диапазоне for l oop, который перебирает все элементы в контейнере с правой стороны :. То есть l oop будет повторяться три раза, при этом t будет равно "twone", "one" и "two" в этом порядке.

Таким образом, циклы будут искать "twone", "one" и "two" внутри строки s, замените средний символ подстрок ("twone", "one" и "two") внутри s одним '?' символом и pu sh положение этого '?' символа в векторе r.

Например, если входное значение в s равно "someone with the number two", то результатом будет строка "someo?e with the number t?o" и вектор r должен содержать значения 5 и 25 (которые будут напечатаны как 6 и 26 из-за + 1).

Вот пример shoing именно это.

1 голос
/ 19 января 2020

Просто запустите код, вставив в него вывод промежуточных результатов.

Вот демонстрационная программа.

#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';
}

Предположим, что пользователь ввел строку onetwoone. Поэтому внутренний l oop ищет во введенной строке все вхождения слов "twone", "one", "two" последовательно.

Для данной строки слово "twone" не найдено.

Слово "one" находится в позиции 0. Это утверждение

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

средний символ найденного слова во введенной строке со знаком '?'.

Таким образом, это добавленное утверждение

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

выводит

0: o?etwoone

Положение знака '?' (число 1) сохраняется в векторе.

Затем в пределах l oop слово "one" встречается второй раз. И снова средний символ найденного слова заменяется на '?'. Таким образом, это утверждение

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

выводит

6: o?etwoo?e

Положение знака '?' (число 7) хранится в векторе.

Итак, на данный момент у нас есть следующий вывод

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

Слово "one" больше не найдено.

Слово "two" встречается только один раз в данной строке. ТАК что на выходе получается

3: o?et?oo?e

Положение '?', равное 4, сохраняется в векторе.

Теперь в этот момент у нас есть следующий вывод

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

производится внутренним l oop.

Таким образом, в результате во введенной строке обнаруживаются три вхождения слов.

Таким образом, эти утверждения

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

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

output

3
2 8 5 

Последние значения соответствуют выражениям rr + 1, то есть сохраненным позициям знака '?' plus 1.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...