无法理解此代码中使用的 for 循环

Cannot understand the for loops used in this code

当我遇到这个用 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;

我是这门语言的新手,无法理解第二个(嵌套的)for 循环和第三个 for 循环中发生的事情。有人可以帮我理解吗?

第一个和第三个循环是range-based for loops

第一个循环遍历一个字符串容器。所以 t 依次取值 "twone""one""two"

第二个循环搜索字符串 s 中出现的所有 t(每次搜索都从找到的前一次出现的位置 pos 开始)。只要找到一个元素,它就会:

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

push_back() 存储在整数向量中找到的每个出现的中间位置。

第三个循环迭代这个存储位置的向量并打印元素(位置计数从 0 开始,+1 移动打印位置,就好像计数从 1 开始一样)。

只是 运行 在其中插入输出 pf 中间结果的代码。

这是一个演示程序。

#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。因此,内循环在输入的字符串中依次搜索所有出现的单词 "twone"、"one"、"two"。

对于给定的字符串,找不到单词 "twone"

在位置 0 处找到单词 "one"。此语句

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

在输入的字符串中以符号'?'找到的单词的中间字符。

因此添加了这条语句

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

产出

0: o?etwoone

符号“?”的位置(数字 1)存储在向量中。

然后在循环中第二次找到单词 "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

由内部循环产生。

因此,在输入的字符串中找到了三个单词。

因此这些陈述

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

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

输出

3
2 8 5 

最后的值对应于表达式 rr + 1,即符号的存储位置 '?' 加 1。

尝试和理解复杂代码的主要方法之一是尝试简化它。它还有助于了解所涉及的函数的作用,因此 a reference to std::string::find 有助于阅读。

首先,让我们跳过正文,只关注循环本身:

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

所有for循环都可以看作是while循环,while循环可能更容易理解和理解,所以我们将其转换为这样的[=21] =]循环:

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);
}

现在循环本身很简单,观察它我们发现基本上是尝试在字符串 s 中找到子字符串 t。只要在 s.

中找到子字符串 t,循环就会继续

既然我们解构了循环本身,让我们看一下循环体及其作用:

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] = '?';

s中子串t的中间字符替换为字符'?'.

第二条语句

r.push_back(new_pos);

'?' 的位置推入向量 r


现在我们终于把内循环(上面解释的)放到外循环的上下文中:

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

这是一个 range-based for loop,它循环遍历 : 右侧容器中的所有元素。也就是说,循环将迭代三次,t 依次等于 "twone""one""two"

所以循环会在字符串s中搜索"twone""one""two",替换子字符串的中间字符("twone", "one" and "two") inside s with a single '?' character, and push the position of that '?' character into the vector r.

例如,如果 s 中的输入是 "someone with the number two",则结果将是字符串 "someo?e with the number t?o",向量 r 应包含值 525(由于 + 1,将打印为 626)。

Here's an example 正是如此。