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

Posted

技术标签:

【中文标题】无法理解此代码中使用的 for 循环【英文标题】:Cannot understand the for loops used in this code 【发布时间】:2020-01-19 12:26:35 【问题描述】:

我正在尝试一个问题的解决方案,当我遇到这个用 C++ 编写的代码 sn-p 时:

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 循环中发生了什么。谁能帮我理解一下?

【问题讨论】:

请告诉我们问题是什么? 虽然阅读其他人的代码有时对新程序员很有用,但通常很难,因为棘手或“聪明”的代码可能不利于学习。学习一门新的编程语言最好的方法是“做”,即你应该尝试自己实现算法。从“做”开始,当你的语言变得更好时,你可以更轻松地阅读其他代码并用作参考(仅供参考,不要落入the cargo cult programming trap)。 【参考方案1】:

第一个和第三个循环是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 开始一样)。

【讨论】:

【参考方案2】:

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

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

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

所有for 循环都可以看作while 循环,而while 循环可能更容易理解和遵循,因此我们将其转换为这样的while 循环:

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,它循环遍历:右侧容器中的所有元素。也就是说,循环将迭代 3 次,t 依次等于"twone""one""two"

所以循环会在s内搜索"twone""one""two",替换@内的子字符串("twone""one""two")的中间字符987654361@ 与单个 '?' 字符,并将该 '?' 字符的位置推入向量 r。

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

Here's an example 就是这样。

【讨论】:

感谢您的解释,循环中的另一个疑问。 for (size_t pos = 0; (pos = s.find(t, pos)) != std::string::npos;)pos 是什么类型的变量? npos 也一样,上面写着 size_t pos=0size_t 是类型吗? @McSkelly 是的,size_t 是标准类型(但不像 intdouble 那样内置)。 size_t 是用于大小、索引等的无符号整数类型。它是sizeof 运算符的结果类型。【参考方案3】:

只需运行在其中插入输出 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"

单词"one"位于位置0。此语句

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。

【讨论】:

以上是关于无法理解此代码中使用的 for 循环的主要内容,如果未能解决你的问题,请参考以下文章

for循环和R中的函数

反应中的简单for循环此代码在for循环中给出错误

Python中对于多个for循环和变量的列表理解

如何使用 for 循环优化此代码

无法理解 OpenMP 代码中的一些意外行为

在 Python 中将列表理解转换为 For 循环