现在要删除与谓词匹配的元素吗?
我有一个字符串源容器,我想从源容器中删除与谓词匹配的任何字符串,并将它们添加到目标容器中。
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;
}
I have a source container of strings I want to remove any strings from the source container that match a predicate and add them into the destination container.
remove_copy_if
and other algorithms can only reorder the elements in the container, and therefore have to be followed up by the erase
member function. My book (Josuttis) says that remove_copy_if
returns an iterator after the last position in the destination container. Therefore if I only have an iterator into the destination container, how can I call erase
on the source container? I have tried using the size of the destination to determine how far back from the end of the source container to erase from, but had no luck. I have only come up with the following code, but it makes two calls (remove_if
and remove_copy_if
).
Can someone let me know the correct way to do this? I'm sure that two linear calls is not
the way to do this.
#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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
出于对 Fred 辛勤工作的尊重,让我补充一点:在抽象级别上,
move_if
与remove_copy_if
没有什么不同。 唯一的实现级别更改是end()
迭代器。 您仍然没有得到任何erase()
。 接受的答案不会erase()
匹配的元素——OP问题陈述的一部分。至于OP的问题:你想要的是一个in-放置拼接。 对于列表来说这是可能的。 但是,对于
向量
,这是行不通的。 了解迭代器何时、如何以及为何失效。 您必须采用两遍算法。来自 SGI 的文档
remove_copy_if
:因此不会发生相对重新排序。 此外,这是一个副本,这意味着您的情况下来自
Source
vector
的元素将被复制到Container vector
中。您需要使用不同的算法,称为
remove_if
:因此,只需将
remove_copy_if
调用更改为:就可以了。 请记住,您的
字符串向量
的范围不再是由迭代器[first(), end())
定义的,而是由[first ()、new_last)
。如果您愿意,您可以通过以下方式删除剩余的
[new_last, end())
:现在,您的
vector
已被缩短,并且您的end( )
和new_last
是相同的(最后一个元素之后),因此您可以一如既往地使用:在控制台上打印字符串(
stdout
)。With all due respect to Fred's hard work, let me add this: the
move_if
is no different thanremove_copy_if
at an abstract level. The only implementation level change is theend()
iterator. You are still not getting anyerase()
. The accepted answer does noterase()
the matched elements -- part of the OP's problem statement.As for the OP's question: what you want is an in-place splice. This is possible for lists. However, with
vectors
this will not work. Read about when and how and why iterators are invalidated. You will have to take a two pass algorithm.From SGI's documentation on
remove_copy_if
:So no relative reordering takes place. Moreover, this is a copy, which means the elements from
Source
vector
in your case, is being copied to theContainer vector
.You need to use a different algorithm, called
remove_if
:So, just change that
remove_copy_if
call to:and you're all set. Just keep in mind, your
Strings vector
's range is no longer that defined by the iterators[first(), end())
but rather by[first(), new_last)
.You can, if you want to, remove the remaining
[new_last, end())
by the following:Now, your
vector
has been shortened and yourend()
andnew_last
are the same (one past the last element), so you can use as always:to get a print of the strings on your console (
stdout
).我明白您的观点,您希望避免对源容器进行两次传递。 不幸的是,我不相信有一个标准算法可以做到这一点。 您可以创建自己的算法,一次性将元素复制到新容器并从源容器中删除(与remove_if 含义相同;之后必须进行擦除)。 您的容器大小和性能要求将决定创建此类算法的工作是否会比进行两次传递更好。
编辑:我想出了一个快速实现:
编辑:
也许人们对“通行证”的含义感到困惑。 在OP的解决方案中,有一个对remove_copy_if()的调用和一个对remove_if()的调用。 其中每个都将遍历整个原始容器。 然后调用erase()。 这将遍历从原始容器中删除的所有元素。
如果我的算法用于将删除的元素复制到新容器(使用 begin() 输出迭代器的原始容器将不起作用,如 dirkgently 所示),它将执行一次传递,将删除的元素复制到新容器back_inserter 或某种此类机制的手段。 仍然需要擦除,就像remove_if() 一样。 原始容器的一次传递被消除,我相信这就是OP所追求的。
I see your point, that you'd like to avoid doing two passes over your source container. Unfortunately, I don't believe there's a standard algorithm that will do this. It would be possible to create your own algorithm that would copy elements to a new container and remove from the source container (in the same sense as remove_if; you'd have to do an erase afterward) in one pass. Your container size and performance requirements would dictate whether the effort of creating such an algorithm would be better than making two passes.
Edit: I came up with a quick implementation:
Edit:
Maybe there is confusion in what is meant by a "pass". In the OP's solution, there is a call to remove_copy_if() and a call to remove_if(). Each of these will traverse the entirety of the original container. Then there is a call to erase(). This will traverse any elements that were removed from the original container.
If my algorithm is used to copy the removed elements to a new container (using begin() the original container for the output iterator will not work, as dirkgently demonstrated), it will perform one pass, copying the removed elements to the new container by means of a back_inserter or some such mechanism. An erase will still be required, just as with remove_if(). One pass over the original container is eliminated, which I believe is what the OP was after.
将有copy_if和remove_if。
最好理解代码,其中Predicate类在存在某些内容时回答“true”。 在这种情况下,您不需要 not1 两次。
因为 std::find 从一开始就查找不是必需的子字符串,因此您需要将“beginning with 1”更改为“with 1”,以避免将来对代码产生误解。
There will be copy_if and remove_if.
It is better to understand code where Predicate class answering "true" if something is present. In that case you won't need not1 two times.
Because std::find looks for substring not obligatory from the begin you need to change "beginning with 1" to "with 1" to avoid future misunderstanding of your code.
remove_* 算法不擦除元素的全部原因是因为单独通过迭代器“擦除”元素是不可能的。 无法通过迭代器获取容器
这一点在《Effective STL》一书中有更详细的解释
。remove_
使用'
copy_if
',然后是'remove_if
'。remove_copy_if
不会修改源。在列表上,您可以做得更好 - 重新排序,然后拼接。
The whole reason why the remove_* algorithms do not erase elements is because it is impossible to "erase" an element by the iterator alone. You can't get container by iterator
This point is explained in more details in the book "Effective STL"
Use '
copy_if
', followed by 'remove_if
'.remove_copy_if
does not modify the source.On lists you can do better - reordering followed by splice.
如果您不介意将字符串放在同一个容器中,并且只用一个迭代器来分隔它们,则此代码可以工作。
If you don't mind having your strings in the same container, and having just an iterator to separate them, this code works.
remove*() 并不真正删除元素,它只是对它们重新排序并将它们放在集合的末尾,并在同一容器中返回一个 new_end 迭代器,指示新的结尾在哪里。 然后,您需要调用擦除以从向量中删除该范围。
remove_if() 执行相同的操作,但带有谓词。
remove_copy_if() 只会复制与谓词不匹配的元素,保持源向量完整并为您提供目标向量上的结束迭代器,以便您可以缩小它。
remove*() don't relally remove elements, it simply reorders them and put them at the end of the collection and return a new_end iterator in the same container indicating where the new end is. You then need to call erase to remove the range from the vector.
remove_if() does the same but with a predicate.
remove_copy_if() will only copy the elements NOT matching the predicate, leaving the source vector intact and providing you with the end iterator on the target vector, so that you can shrink it.