remove_if() 究竟是如何工作的?
How exactly remove_if() works?
我参考了很多资源,但无法理解它究竟是如何修改容器中的元素的部分。
我已经写了一些代码来尝试理解,谁能解释一下发生了什么?
#include<bits/stdc++.h>
using namespace std;
bool test(char c)
{
return (c=='a');
}
int isPalindrome(string A)
{
remove_if(A.begin(), A.end(), test);
cout<<A<<'\n';
}
int main()
{
string A="aaaaabbbbb";
isPalindrome(A);
return 0;
}
根据我目前的理解,它应该将所有 a 字符移到 b 的右侧,如果我们尝试打印它,我们应该得到
bbbbbaaaaa
相反,我得到
bbbbbbbbbb
如果需要,该算法只是将等于 'b' 的元素移动或复制(取决于使用的迭代器)到存储等于 'a' 的元素的位置。
它不交换容器的元素。
是std::remove_if
,不是std::move_if
。它只是从 test
函数中删除所有 return 为真的元素。现在,为什么额外的 b
s?好吧,你不应该看到他们。 std::remove_if
return 是一个标记序列新结束的迭代器,您应该使用 return 值相应地调整容器的大小。假设我们有一个字符串 "Hello, World!"
,我们需要从中删除所有 l
。下面是一个示例程序,展示了执行此操作的一种方法:
#include <algorithm>
#include <string>
#include <iostream>
int main()
{
std::string a = "Hello, World!";
a.resize(std::distance(
a.begin(),
std::remove_if(a.begin(), a.end(), [](char c){return c == 'l';})
));
std::cout << a << '\n';
}
输出:
Heo, Word!
您对 remove_if
的工作方式是正确的。然而,它 returns 是一个迭代器,它指向第一个被移除的元素,它与最后一个未被移除的元素相同。然后你需要删除被移除的元素以获得你想要的答案。因此,在函数 isPalindrome
中,将 remove_if
行替换为
const auto new_end = remove_if(A.begin(), A.end(), test);
A.erase(new_end, A.end());
然后你会得到bbbbb
。
这种先调用 remove_if
后调用 erase
的模式经常发生。这两条线通常合二为一,称为“删除-擦除成语”。在您的情况下,将 remove_if
行替换为
A.erase( remove_if(A.begin(), A.end(), test), A.end() );
According to my understanding so far it should move all the a characters to the right of b
差不多。它做相反的事情:它(有点)将 b 字符移动到 a 的左边。更准确地说,它在删除的元素上“移动赋值”。
因此,左边的元素是 从 右边(在左边的 a 之上)移动的 b,右边的元素是 b被移到 到 左边。由于移动 char 与复制相同,即它不会修改从中移动的参数,因此左元素和右元素都是 b。
由于该算法的预期用例是删除 a,因此容器中没有任何 a 不是问题。请注意,对于不同的输入,一些 a 可能会保留在容器的右端。右边的元素要么是移到左边的元素的残余(不是 a 的),要么是因为要删除它们而没有移动的元素(a 的)。
P.S。您现在可以在 C++20(或将来,假设您已标记 C++17)中使用:
,而不是以前在 GregReese 的 and in François Andrieux's 中显示的惯用模式
std::erase_if(A, test);
我在几条评论中都表达了同样的观点,但我认为需要更详细地解决这个问题。
std::remove
和 std::remove_if
等算法适用于 序列 ,即由一对迭代器指定的元素。第一个迭代器指向序列的第一个元素,第二个迭代器指向元素序列的末尾。实际上,算法从序列中后面的位置移动或复制元素以覆盖正在删除的元素。
请注意,上一段没有使用容器这个词。容器是管理元素序列的一种方式,但不是唯一的方式。算法对容器一无所知。 remove
和 remove_if
无法更改他们一无所知的容器的大小。
将元素传递给 remove
或 remove_if
后查看容器时看到的是修改后的序列(从容器的开头到迭代器指向的元素算法返回),然后是剩下的任何东西。对于像 char
这样的简单类型,这个残基可能就是开头的任何内容。对于具有非平凡移动赋值运算符的更复杂类型,剩余部分由移动赋值留下的任何内容组成。对于 C++ 标准库中定义的类型,这意味着剩余由处于未指定但有效状态的对象组成。对于在标准库以外的地方定义的类型,它们将处于设计者选择的任何状态。
我参考了很多资源,但无法理解它究竟是如何修改容器中的元素的部分。 我已经写了一些代码来尝试理解,谁能解释一下发生了什么?
#include<bits/stdc++.h>
using namespace std;
bool test(char c)
{
return (c=='a');
}
int isPalindrome(string A)
{
remove_if(A.begin(), A.end(), test);
cout<<A<<'\n';
}
int main()
{
string A="aaaaabbbbb";
isPalindrome(A);
return 0;
}
根据我目前的理解,它应该将所有 a 字符移到 b 的右侧,如果我们尝试打印它,我们应该得到
bbbbbaaaaa
相反,我得到
bbbbbbbbbb
如果需要,该算法只是将等于 'b' 的元素移动或复制(取决于使用的迭代器)到存储等于 'a' 的元素的位置。
它不交换容器的元素。
是std::remove_if
,不是std::move_if
。它只是从 test
函数中删除所有 return 为真的元素。现在,为什么额外的 b
s?好吧,你不应该看到他们。 std::remove_if
return 是一个标记序列新结束的迭代器,您应该使用 return 值相应地调整容器的大小。假设我们有一个字符串 "Hello, World!"
,我们需要从中删除所有 l
。下面是一个示例程序,展示了执行此操作的一种方法:
#include <algorithm>
#include <string>
#include <iostream>
int main()
{
std::string a = "Hello, World!";
a.resize(std::distance(
a.begin(),
std::remove_if(a.begin(), a.end(), [](char c){return c == 'l';})
));
std::cout << a << '\n';
}
输出:
Heo, Word!
您对 remove_if
的工作方式是正确的。然而,它 returns 是一个迭代器,它指向第一个被移除的元素,它与最后一个未被移除的元素相同。然后你需要删除被移除的元素以获得你想要的答案。因此,在函数 isPalindrome
中,将 remove_if
行替换为
const auto new_end = remove_if(A.begin(), A.end(), test);
A.erase(new_end, A.end());
然后你会得到bbbbb
。
这种先调用 remove_if
后调用 erase
的模式经常发生。这两条线通常合二为一,称为“删除-擦除成语”。在您的情况下,将 remove_if
行替换为
A.erase( remove_if(A.begin(), A.end(), test), A.end() );
According to my understanding so far it should move all the a characters to the right of b
差不多。它做相反的事情:它(有点)将 b 字符移动到 a 的左边。更准确地说,它在删除的元素上“移动赋值”。
因此,左边的元素是 从 右边(在左边的 a 之上)移动的 b,右边的元素是 b被移到 到 左边。由于移动 char 与复制相同,即它不会修改从中移动的参数,因此左元素和右元素都是 b。
由于该算法的预期用例是删除 a,因此容器中没有任何 a 不是问题。请注意,对于不同的输入,一些 a 可能会保留在容器的右端。右边的元素要么是移到左边的元素的残余(不是 a 的),要么是因为要删除它们而没有移动的元素(a 的)。
P.S。您现在可以在 C++20(或将来,假设您已标记 C++17)中使用:
,而不是以前在 GregReese 的std::erase_if(A, test);
我在几条评论中都表达了同样的观点,但我认为需要更详细地解决这个问题。
std::remove
和 std::remove_if
等算法适用于 序列 ,即由一对迭代器指定的元素。第一个迭代器指向序列的第一个元素,第二个迭代器指向元素序列的末尾。实际上,算法从序列中后面的位置移动或复制元素以覆盖正在删除的元素。
请注意,上一段没有使用容器这个词。容器是管理元素序列的一种方式,但不是唯一的方式。算法对容器一无所知。 remove
和 remove_if
无法更改他们一无所知的容器的大小。
将元素传递给 remove
或 remove_if
后查看容器时看到的是修改后的序列(从容器的开头到迭代器指向的元素算法返回),然后是剩下的任何东西。对于像 char
这样的简单类型,这个残基可能就是开头的任何内容。对于具有非平凡移动赋值运算符的更复杂类型,剩余部分由移动赋值留下的任何内容组成。对于 C++ 标准库中定义的类型,这意味着剩余由处于未指定但有效状态的对象组成。对于在标准库以外的地方定义的类型,它们将处于设计者选择的任何状态。