Использование алгоритмов STL для унарных функций

Поэтому мне часто нужно выполнить какой-нибудь алгоритм STL для ряда элементов, и вместо функции сравнения я хотел бы передать унарную функцию f.

Например, я хотел бы написать что-то вроде этого

std::max_element(begin(range), end(range), f);

чтобы найти максимальный элемент в диапазоне после применения f. Мой текущий обходной путь выглядит примерно так:

std::max_element(begin(range), end(range, [&f](auto a, auto b){ return f(a) < f(b); });

На первый взгляд может показаться, что это не проблема. Но f может быть самим лямбда-выражением или чем-то более сложным, чем просто f. У меня есть две проблемы с этим фрагментом кода: а) Это подвержено ошибкам, потому что можно случайно написать f (a) < f (a) (особенно если оно более сложное и используется копия и прошлое). Это проблема дублирования кода б) Это не очень хорошо выражает намерение. Если я хочу отсортировать по функции, я не хочу иметь дело со сравнением.

К сожалению, я не нашел хорошего решения для такого рода проблем в стандартной библиотеке (или даже в boost), поэтому я хотел бы спросить вас, каково ваше решение этой проблемы. Есть ли причина отсутствия этой перегрузки в алгоритмах?

Итератор преобразования Boost кажется тем, что вы ищете. Другими словами, следует адаптировать итераторы, которые вы передаете max_element, а не компаратор.

john 24.12.2020 11:10

В стандартной библиотеке это невозможно с использованием существующих функций. Вам нужна комбинация std::transform и max_element. Для этого вы можете написать очень простую шаблонную функцию-оболочку. Это не сложно. 3 строки кода. . .

A M 24.12.2020 11:21

Спасибо за эту идею! Я до сих пор не вижу, как избежать проблемы. Приведенный там пример также имеет проблему дублирования кода.

Henk 24.12.2020 11:22

Стандартные библиотечные функции не могут удовлетворить все возможные требования. Всегда будут моменты, когда вам нужно что-то немного другое. Вы всегда можете написать свою собственную функцию для удовлетворения ваших конкретных потребностей. Вы по-прежнему можете использовать стандартную библиотеку для выполнения основной части этой задачи.

Galik 24.12.2020 11:36

Это определенно правда. Однако, по крайней мере для меня, я никогда не использовал какую-либо другую функцию сравнения, кроме сравнения значения вывода унарной функции. Есть ли способ перегрузить все алгоритмы STL функцией сравнения одной собственной функцией?

Henk 24.12.2020 12:24

Отвечает ли это на ваш вопрос? Требуется очень общая функция argmax на C++

alex_noname 24.12.2020 12:30

К сожалению, не совсем. Это решает проблему только для определенного алгоритма. Максимум - это просто пример, я бы хотел, чтобы такое поведение звучало для каждого алгоритма.

Henk 24.12.2020 12:50
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
7
142
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Используя диапазоны С++ 20, вы можете сделать:

std::ranges::max_element(range | std::views::transform(f));

Почему бы не передать проекцию (3-й аргумент) в max_element?

Marc Glisse 24.12.2020 13:01

Я думаю, что в конце концов это то же самое, но да, это тоже сработает max_element(range, {}, f);

Yamahari 24.12.2020 13:11

Лично я бы предпочел первую версию, потому что вызов для преобразования самостоятельно документирует код, а вторая версия непонятна, если вы с ней не знакомы.

Yamahari 24.12.2020 13:20

К сожалению, в настоящее время у меня есть доступ только к С++ 17, но это прекрасно решит проблему! Я бы предпочел вторую версию, так как можно предположить знакомство со стандартной библиотекой.

Henk 24.12.2020 13:32

Вы можете взглянуть на библиотеку диапазонов v3 Эрика Ниблера, она должна работать так и является С++ 17 (я думаю), и вы сможете зайти и заменить на stl, как только у вас будет доступ к С++ 20

Yamahari 24.12.2020 14:01

Одна вещь, которую вы можете сделать, это создать свой собственный общий компаратор, который принимает унарную функцию для выполнения преобразования:

// Generic comparator
template<typename T, typename F>
struct transform_comparator_type
{
    transform_comparator_type(F f): f(f) {}
    bool operator()(T const& a, T const& b) const { return f(a) < f(b); }
    F f;
};

// Helper function to deduce the type of F
template<typename T, typename F>
auto transform_comparator(F f)
{
    return transform_comparator_type<T, F>(f);
}

int main()
{
    std::vector<int> v{1, 4, 3, 6, 0};

    auto e = std::max_element(std::begin(v), std::end(v),
        transform_comparator<int>([v](int i){ return 2 * i; }));

    // etc...
}

Отредактировано, чтобы добавить:

Фактически тип можно вывести из возвращаемого типа предоставленной функции преобразования, поэтому вам не нужна вспомогательная функция. Вместо этого вы можете сделать это:

template<typename F>
struct transform_comparator
{
    using T = decltype(F()({})); // deduce T from return type of F
    transform_comparator(F f): f(f) {}
    bool operator()(T const& a, T const& b) const { return f(a) < f(b); }
    F f;
};

Это выглядит как хорошее решение для С++ 17, а также решает еще более общую проблему. Я также искал что-то подобное в STL или boost, но не нашел, что, по-моему, довольно странно, потому что это распространенная проблема (или нет?).

Henk 24.12.2020 13:38

Я думаю, что большинство людей просто написали бы компаратор как лямбду там, где им это нужно.

Marc Glisse 24.12.2020 16:14

Другие вопросы по теме