遍历除当前对象之外的所有其他对象
Iterate over all the other objects excluding the current one
在许多应用程序中,有必要计算一系列对象之间的相互交互,即对于序列中的每个对象,遍历所有其他对象 except/skipping/excluding 当前对象。
这是我目前的解决方案:
std::vector<Object> objects;
for( auto icurr=objects.begin(); icurr!=objects.end(); ++icurr )
{// Iterate over all the other objects excluding this one
for( auto iother=objects.begin(); iother!=icurr; ++iother )
icurr->interact_with(*iother);
for( auto iother=icurr+1; iother!=objects.end(); ++iother )
icurr->interact_with(*iother);
}
但这并不令我满意,因为它很麻烦,而且最重要的是重复了循环体。
我也不喜欢平庸的选择:
for( auto icurr=objects.begin(); icurr!=objects.end(); ++icurr )
{
for( auto iother=objects.begin(); iother!=objects.end(); ++iother )
if(icurr!=iother) icurr->interact_with(*iother);
}
因为有一个可以避免的测试;在那种情况下,我宁愿写这样的东西:
for( Object& curr : objects )
{
for( const Object& other : objects.excluded(curr) )
curr.interact_with(other);
}
如果它具有相同的性能,那将大大提高可读性。我可以利用最新的 c++ 标准来提高代码的可读性吗?有什么建议吗?
免责声明:这是一个编码风格问题,试图不断更新最佳实践,学习和改进。
我不认为我会编写一个整体单独的排除函数,因为那样你最终会重新分配整个对象向量减去一个元素。不要过度设计基于索引的 for 循环可以完成的工作。
for (size_t i = 0; i < objects.size(); i++) {
for (size_t j = 0; j < objects.size(); j++) {
if (i == j) continue;
objects[i].interact_with(objects[j]);
}
}
对于范围库,您可以执行以下操作:
for (const Object& curr : objects) {
for (const Object& other : objects | ranges::filter([&](const Object& obj) { return &obj != curr; }) )
curr.interact_with(other);
}
但这确实会做额外的比较。
您的第一个版本似乎是最佳的。
使用 std::span
(C++20)(或重新实现的版本),为了避免重复,您可能会这样做:
std::vector<Object> objects;
for (auto it = objects.begin(); it != objects.end(); ++it) {
for (auto range : {std::span{objects.begin(), it}, std::span{it + 1, objects.end()}}) {
for (const auto& other : range) {
it->interact_with(other);
}
}
}
在许多应用程序中,有必要计算一系列对象之间的相互交互,即对于序列中的每个对象,遍历所有其他对象 except/skipping/excluding 当前对象。 这是我目前的解决方案:
std::vector<Object> objects;
for( auto icurr=objects.begin(); icurr!=objects.end(); ++icurr )
{// Iterate over all the other objects excluding this one
for( auto iother=objects.begin(); iother!=icurr; ++iother )
icurr->interact_with(*iother);
for( auto iother=icurr+1; iother!=objects.end(); ++iother )
icurr->interact_with(*iother);
}
但这并不令我满意,因为它很麻烦,而且最重要的是重复了循环体。 我也不喜欢平庸的选择:
for( auto icurr=objects.begin(); icurr!=objects.end(); ++icurr )
{
for( auto iother=objects.begin(); iother!=objects.end(); ++iother )
if(icurr!=iother) icurr->interact_with(*iother);
}
因为有一个可以避免的测试;在那种情况下,我宁愿写这样的东西:
for( Object& curr : objects )
{
for( const Object& other : objects.excluded(curr) )
curr.interact_with(other);
}
如果它具有相同的性能,那将大大提高可读性。我可以利用最新的 c++ 标准来提高代码的可读性吗?有什么建议吗? 免责声明:这是一个编码风格问题,试图不断更新最佳实践,学习和改进。
我不认为我会编写一个整体单独的排除函数,因为那样你最终会重新分配整个对象向量减去一个元素。不要过度设计基于索引的 for 循环可以完成的工作。
for (size_t i = 0; i < objects.size(); i++) {
for (size_t j = 0; j < objects.size(); j++) {
if (i == j) continue;
objects[i].interact_with(objects[j]);
}
}
对于范围库,您可以执行以下操作:
for (const Object& curr : objects) {
for (const Object& other : objects | ranges::filter([&](const Object& obj) { return &obj != curr; }) )
curr.interact_with(other);
}
但这确实会做额外的比较。
您的第一个版本似乎是最佳的。
使用 std::span
(C++20)(或重新实现的版本),为了避免重复,您可能会这样做:
std::vector<Object> objects;
for (auto it = objects.begin(); it != objects.end(); ++it) {
for (auto range : {std::span{objects.begin(), it}, std::span{it + 1, objects.end()}}) {
for (const auto& other : range) {
it->interact_with(other);
}
}
}