C ++标准库提供将比较器传递给std::sort
。但是,我的代码中有很多情况,我希望通过函数T
对f
个对象的列表进行排序。像这样的比较器将是一个有效的选项:
bool compare(const T& a, const T& b) {
return f(a) < f(b);
}
但这不是最佳选择。 f
评估缓慢,但会为具有相同T
对象的每个调用返回相同的值。所以我宁愿做的是为范围内的每个对象计算一次f
,然后使用这些结果对它们进行排序。
我的目标是编写这个函数(我无法做到):
template <typename IterT, typename Transformation>
void sort(IterT left, IterT right, Transformation f) { /* ? */ }
在此次通话后,f(*iter) <= f(*std::next(iter))
代表序列iter
至left
中的所有right
。
此外,该功能应满足这些要求:
T
类型的任何其他对象。f
完全std::distance(left, right)
。(C ++ 11是首选; C ++ 14也可以)
答案 0 :(得分:3)
所以你想要的是Schwartzian transform的C ++实现。我在几行代码中没有简单的解决方案,但我确实在我的C ++ 14库中实现了Schwartzian transform utility。不幸的是,它依赖于代理迭代器,它不由std::sort
处理(至少在Ranges TS之前),但您可以使用库中的任何其他分类器。以下是您可以编写问题中提到的sort
函数的方法:
#include <cpp-sort/adapters/schwartz_adapter.h>
#include <cpp-sort/sorters/default_sorter.h>
template <typename IterT, typename Transformation>
void sort(IterT left, IterT right, Transformation f)
{
using sorter = cppsort::schwartz_adapter<cppsort::default_sorter>;
sorter{}(left, right, f);
}
以这种方式调用时,分拣机将跨越[left, right)
并创建将迭代器std::distance(left, right)
与it
相关联的f(*it)
对。然后它将使用传递的排序器(在上面的示例中为default_sorter
,在编写本文时为pattern-defeating quicksort)来对对的集合进行排序。引擎盖下使用Proxy iterators,以便在交换对时交换原始集合的元素。
我不会说这很简单,但它应该可以解决你的问题。如果您不想依赖外部图书馆,您仍然可以从the soure code获取灵感。它是在许可许可下,所以如果你需要使用它,你可以用它做任何你想做的事。
无论如何,看起来它主要满足你的要求:
T
的其他实例(除非f
返回T
的新实例,因为它存储了f
的返回值。 / LI>
f
正好适用std::distance(left, right)
。std::sort
,因为std::sort
目前还不够聪明,但它可以使用等效的算法而不用你必须自己写。答案 1 :(得分:0)
如果您想坚持std::sort
,只需编写一个比较器,为T
的每个实例缓存函数的值。
示例:
struct Foo {
int value;
};
// Replace with your CPU time intensive f() function
int evaluate(const Foo& foo) {
std::cout << "Evaluating complex function on an instance of Foo..." << std::endl;
return foo.value;
}
bool compare(const Foo& left, const Foo& right) {
static std::unordered_map<Foo, int> cache;
auto leftIt = cache.find(left);
auto rightIt = cache.find(right);
if (leftIt == cache.end())
leftIt = cache.emplace(left, evaluate(left)).first;
if (rightIt == cache.end())
rightIt = cache.emplace(right, evaluate(right)).first;
return (*leftIt).second < (*rightIt).second;
}
您可以在此处找到完整示例:https://gist.github.com/PandarinDev/ee75b095c4cc256a88496f1985bf57ba
这种方式evaluate(const Foo&);
(在您的情况下为f(T)
)只会运行N次,其中N = the number of unique instances of Foo
。
编辑如下面评论中所述,如果T
实例的副本进入地图是您的问题,您可以使用唯一标识符 - 例如地址你的对象 - 作为一个键而不是对象本身。