我有一个字符串的源容器我想从源容器中删除与谓词匹配的任何字符串,并将它们添加到目标容器中。
remove_copy_if
和其他算法只能重新排序容器中的元素,因此必须由erase
成员函数跟进。我的书(Josuttis)说remove_copy_if
在目标容器中的最后一个位置之后返回一个迭代器。因此,如果我只在目标容器中有一个迭代器,我如何在源容器上调用erase
?我已经尝试使用目标的大小来确定从源容器的末尾回去多远,但没有运气。我只提出了以下代码,但它会进行两次调用(remove_if
和remove_copy_if
)。
有人能让我知道正确的方法吗?我确定两个线性调用不是 这样做的方法。
#include <iostream>
#include <iterator>
#include <vector>
#include <string>
#include <algorithm>
#include <functional>
using namespace std;
class CPred : public unary_function<string, bool>
{
public:
CPred(const string& arString)
:mString(arString)
{
}
bool operator()(const string& arString) const
{
return (arString.find(mString) == std::string::npos);
}
private:
string mString;
};
int main()
{
vector<string> Strings;
vector<string> Container;
Strings.push_back("123");
Strings.push_back("145");
Strings.push_back("ABC");
Strings.push_back("167");
Strings.push_back("DEF");
cout << "Original list" << endl;
copy(Strings.begin(), Strings.end(),ostream_iterator<string>(cout,"\n"));
CPred Pred("1");
remove_copy_if(Strings.begin(), Strings.end(),
back_inserter(Container),
Pred);
Strings.erase(remove_if(Strings.begin(), Strings.end(),
not1(Pred)), Strings.end());
cout << "Elements beginning with 1 removed" << endl;
copy(Strings.begin(), Strings.end(),ostream_iterator<string>(cout,"\n"));
cout << "Elements beginning with 1" << endl;
copy(Container.begin(), Container.end(),ostream_iterator<string>(cout,"\n"));
return 0;
}
答案 0 :(得分:7)
尽管弗雷德的辛勤工作得到应有的尊重,但我要补充一点:move_if
与remove_copy_if
在抽象层面上没有什么区别。唯一的实现级别更改是end()
迭代器。你还没有得到任何erase()
。 接受的答案不是erase()
匹配的元素 - OP问题陈述的一部分。
关于OP的问题:你想要的是就地拼接。这可用于列表。但是,使用vectors
这将无效。了解迭代器何时以及如何以及为何失效。你必须采用两遍算法。
remove_copy_if
和其他算法只能重新排序容器中的元素,
来自SGI关于remove_copy_if
的文档:
此操作是稳定的,这意味着复制的元素的相对顺序与[first,last]范围内的相对顺序相同。
因此不会发生相对重新排序。此外,这是一个副本,这意味着您案例中Source
vector
的元素正在复制到Container vector
。
如何在源容器上调用erase?
您需要使用另一种名为remove_if
的算法:
remove_if
从每个元素[first, last)
中移除x
范围,以使pred(x)
成立。也就是说,remove_if
返回迭代器new_last
,使得范围[first, new_last)
不包含pred
为真的元素。范围[new_last, last)
中的迭代器仍然可以解除引用,但它们指向的元素未指定。Remove_if
是稳定的,这意味着未删除的元素的相对顺序不变。
因此,只需将remove_copy_if
调用更改为:
vector<string>::iterator new_last = remove_if(Strings.begin(),
Strings.end(),
Pred);
你已经准备好了。请记住,Strings vector
的范围不再是迭代器[first(), end())
定义的范围,而是[first(), new_last)
。
如果您愿意,可以通过以下方式删除剩余的[new_last, end())
:
Strings.erase(new_last, Strings.end());
现在,您的vector
已缩短,end()
和new_last
相同(超过最后一个元素),因此您可以一如既往地使用:
copy(Strings.begin(), Strings.end(), ostream_iterator(cout, "\"));
在控制台上打印字符串(stdout
)。
答案 1 :(得分:5)
我明白你的观点,你要避免对源容器进行两次传递。不幸的是,我不相信有一个标准算法可以做到这一点。可以创建自己的算法,将元素复制到新容器并从源容器中删除(与remove_if相同;之后必须进行擦除)。您的容器大小和性能要求将决定创建此类算法的努力是否优于两次通过。
编辑:我提出了一个快速实施:
template<typename F_ITER, typename O_ITER, typename FTOR>
F_ITER move_if(F_ITER begin, F_ITER end, O_ITER dest, FTOR match)
{
F_ITER result = begin;
for(; begin != end; ++begin)
{
if (match(*begin))
{
*dest++ = *begin;
}
else
{
*result++ = *begin;
}
}
return result;
}
编辑: 也许“通行证”意味着混淆。在OP的解决方案中,调用remove_copy_if()并调用remove_if()。每个都将遍历整个原始容器。然后调用erase()。这将遍历从原始容器中删除的任何元素。
如果我的算法用于将删除的元素复制到新容器(使用begin(),输出迭代器的原始容器将无法工作,如Dirkgently演示),它将执行一次传递,将删除的元素复制到通过back_inserter或某种此类机制的新容器。与remove_if()一样,仍然需要擦除。消除了对原始容器的一次通过,我认为这是OP所追求的。
答案 2 :(得分:2)
将有copy_if和remove_if。
copy_if( Strings.begin(), Strings.end(),
back_inserter(Container), not1(Pred) );
Strings.erase( remove_if( Strings.begin(), Strings.end(), not1(Pred) ),
Strings.end() );
如果存在某些内容,最好理解Predicate类回答“true”的代码。在这种情况下,你不需要两次。
因为std :: find从开头查找子字符串不是必须的,所以你需要将“从1开始”更改为“with 1”,以避免将来误解你的代码。
答案 3 :(得分:1)
remove_ *算法不擦除元素的全部原因是因为单独使用迭代器不可能“擦除”元素。 您无法通过迭代器获取容器 这一点在“有效STL”
使用“copy_if
”,然后使用“remove_if
”。 remove_copy_if
不会修改来源。
在列表上你可以做得更好 - 重新排序然后拼接。
答案 4 :(得分:1)
如果您不介意将字符串放在同一容器中,并且只使用迭代器来分隔它们,则此代码可以正常工作。
#include "stdafx.h"
#include <iostream>
#include <iterator>
#include <vector>
#include <string>
#include <algorithm>
#include <functional>
using namespace std;
class CPred : public unary_function<string, bool>
{
public:
CPred(const string& arString)
:mString(arString)
{
}
bool operator()(const string& arString) const
{
return (arString.find(mString) == std::string::npos);
}
private:
string mString;
};
int main()
{
vector<string> Strings;
Strings.push_back("213");
Strings.push_back("145");
Strings.push_back("ABC");
Strings.push_back("167");
Strings.push_back("DEF");
cout << "Original list" << endl;
copy(Strings.begin(), Strings.end(),ostream_iterator<string>(cout,"\n"));
CPred Pred("1");
vector<string>::iterator end1 =
partition(Strings.begin(), Strings.end(), Pred);
cout << "Elements matching with 1" << endl;
copy(end1, Strings.end(), ostream_iterator<string>(cout,"\n"));
cout << "Elements not matching with 1" << endl;
copy(Strings.begin(), end1, ostream_iterator<string>(cout,"\n"));
return 0;
}
答案 5 :(得分:0)
remove *()不会重新删除元素,它只是重新排序它们并将它们放在集合的末尾,并在同一容器中返回一个new_end迭代器,指示新结束的位置。然后,您需要调用erase来从矢量中删除范围。
source.erase(source.remove(source.begin(), source.end(), element), source.end());
remove_if()使用谓词执行相同的操作。
source.erase(source.remove_if(source.begin(), source.end(), predicate), source.end());
remove_copy_if()只会复制与谓词不匹配的元素,保持源向量不变,并为目标向量提供结束迭代器,以便缩小它。
// target must be of a size ready to accomodate the copy
target.erase(source.remove_copy_if(source.begin(), source.end(), target.begin(), predicate), target.end());