无法理解此代码中使用的 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
应该包含值5
和25
(将打印为6
和 26
因为 + 1
)。
Here's an example 就是这样。
【讨论】:
感谢您的解释,循环中的另一个疑问。for (size_t pos = 0; (pos = s.find(t, pos)) != std::string::npos;)
,pos
是什么类型的变量? npos
也一样,上面写着 size_t pos=0
。 size_t
是类型吗?
@McSkelly 是的,size_t
是标准类型(但不像 int
或 double
那样内置)。 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 循环的主要内容,如果未能解决你的问题,请参考以下文章