Вот эта задача пришла ко мне из обзора кода. Я хочу выбрать минимальное значение из набора, основываясь на специальном предикате сравнения. Вот так:
struct Complex { ... };
float calcReduction(Complex elem);
Complex findMinValueWithPredicates(const std::vector<Complex>& values)
{
auto it = std::min_element(values.begin(), values.end(),
[](const Complex& a, const Complex& b) {
return calcReduction(a) < calcReduction(b);
});
if (it == values.end()) throw std::runtime_error("");
return *it;
}
Здесь я нахожу минимальный элемент, основанный на предикате. Этот предикат вычисляет уменьшение обоих значений до float
, а затем сравнивает эти поплавки. Отлично работает, выглядит аккуратно.
Вы видите проблему? Да, для набора элементов N
calcReduction()
называется 2N
раз, тогда как достаточно вычислить его только N
раз - один раз для каждого элемента.
Одним из способов решения этой проблемы является запись явных вычислений:
Complex findMinValueExplicit(const std::vector<Complex>& values)
{
float minReduction = std::numeric_limits<float>::max();
Complex minValue;
for (Complex value : values)
{
float reduction = calcReduction(value);
if (reduction < minReduction)
{
minReduction = reduction;
minValue = value;
}
}
if (minReduction == std::numeric_limits<float>::max()) throw std::runtime_error("");
return minValue;
}
Он работает нормально, и мы вызываем N
только calcReduction()
. Тем не менее, это выглядит слишком многословным, и намерение не является таким ясным, по сравнению с явным вызовом min_element
. Потому что, когда вы вызываете min_element
, действительно легко догадаться, что вы найдете минимальный элемент, знаете ли.
Единственная идея, которую я имею сейчас, - создать собственный алгоритм, например min_element_with_reduction
, принять диапазон и функцию сокращения. Звучит разумно, но мне интересно, есть ли готовые решения.
Любые идеи о том, как решить эту задачу с явным намерением и некоторыми готовыми решениями? Boost приветствуется. С++ 17 и диапазоны интересны для просмотра.