Существует ли дубликат range :: views :: group_by, который учитывает все элементы, а не только смежные? - PullRequest
0 голосов
/ 31 октября 2019

В C++20 std::ranges можно ожидать получения views::group_by 1 . Это может быть очень удобно, но я нашел проблему во время игры с ним. Из руководства Эрика Ниблера мы можем прочитать, что это " По существу, views::group_by группирует смежные элементы вместе с двоичным предикатом. ". Давайте рассмотрим пример. У меня есть std::vector некоторых int с, и я хочу сгруппировать в два диапазона - представляющих четные и нечетные числа. Мой первоначальный подход состоял в том, чтобы просто сделать:

int main() {
    using namespace ranges;

    std::vector<int> ints = {3, 9, 12, 10, 7, 5, 1, 4, 8};

    for (auto rng : ints | views::group_by(
            [](auto lhs, auto rhs) {
                const bool leftEven = lhs % 2 == 0;
                const bool rightEven = rhs % 2 == 0;

                return (leftEven && rightEven) || (!leftEven && !rightEven);
            })) {
        std::cout << rng << '\n';
    }
}

Но это не может работать. Или, другими словами, это будет работать, но даст неожиданные (для некоторых, я полагаю) результаты для тех, кто знаком с подобными операциями в других языках (или даже API). Вывод этой программы:

[3,9]
[12,10]
[7,5,1]
[4,8]

Четные и нечетные числа не все сгруппированы - это потому, что они не все смежные. 3 и 9 спарены вместе, потому что они ood и смежные . Точно так же (кроме того, чтобы быть даже ) 12 и 10. Но 7, 5 и 1 создадут отдельную группу - они не будут сгруппированы с 3 и 9, и это не то, чего я бы хотел или ожидал.

Чтомы, конечно, могли бы сделать это для partition вектора ints, чтобы упорядочить элементы так, чтобы четные и четные составляли две группы. Проблема в том, что в диапазонах нет views::partition. Это оставляет меня с двумя вариантами, где ни один из них не особенно мне нравится:

1. std</s>ranges::partition перед просмотром вектора:

Вызов:

ranges::partition(ints, [](auto elem) { return elem % 2 == 0; });

непосредственно перед нашим диапазоном на основе for петли, и у нас есть желаемый результат:

[8,4,12,10]
[7,5,1,9,3]

Мне это не нравится, потому что у него нет компоновки - один из ключевых факторов ranges. Я не хочу разделять вектор, если честно. Я хочу напечатать его элементы в двух группах - четные и нечетные.

2. Используйте actions::sort и сортируйте вектор с помощью четно-нечетного компаратора:

int main() {
    using namespace ranges;

    std::vector<int> ints = {3, 9, 12, 10, 7, 5, 1, 4, 8};

    auto evens_first = [](auto lhs, auto rhs) { return lhs % 2 == 0 && rhs % 2 != 0; };

    for (auto rng : (ints |= actions::sort(evens_first)) | views::group_by(
            [](auto lhs, auto rhs) {
                const bool leftEven = lhs % 2 == 0;
                const bool rightEven = rhs % 2 == 0;

                return (leftEven && rightEven) || (!leftEven && !rightEven);
            })) {
        std::cout << rng << '\n';
    }
}

Обратите внимание, что требуются скобки вокруг оператора |=, так как в противном случае оператор составления (|) диапазонов будетоценивается в первую очередь, и мы получим приведенный выше код, печатающий отсортированные элементы вектора, полностью игнорируя группирование ( ??? ).

Этот подход okaaaay , но все еще не велик. Я бы предпочел либо иметь group_by, который мог бы, например, принять значение и вернуть ключ (Java и C# подход к обработке группировки), либо в любом случае принять во внимание весь диапазонили, по крайней мере, actions::partition доступны.

Примечание: я вижу обоснование для views::grouping_by, работающей только со смежными элементами. Это самый эффективный способ - не нужно ничего хранить, не нужно возвращаться или смотреть дальше. Это нормально, а иногда это лучший инструмент для работы. Но я полагаю, что это создает путаницу, поскольку противоречит интуиции людей, которые работали с подобными API в прошлом.

И, наконец, повторяю вопрос - есть ли более краткий способ сделать то, что я хочу, основываясь на примерахи желаемые подходы, которые я предложил?


1 Я не могу найти его на cppreference , но я думаю, что где-то получил подтверждение, что он находится. Исправитьпожалуйста, если я ошибаюсь.

1 Ответ

0 голосов
/ 31 октября 2019

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...