Стоит ли использовать стандартные алгоритмы std :: map + std :: tr1 :: bind +?

Это продолжение моего вопрос со вчерашнего дня. У меня на уме предупреждение Скотта Мейерса о коде, доступном только для записи. Мне в принципе нравится идея использования стандартных алгоритмов для доступа к ключам или значениям std :: map, но требуемый синтаксис немного барочный ИМХО. Допустим, я хочу сбросить все ключи карты в вектор. Учитывая следующие заявления,

typedef std::map<int, int> MyMap;
MyMap m;
std::vector<int> v;

какой код более удобен в обслуживании (т.е. потенциально менее запутан)?

Опция 1:

std::transform(m.begin(),
               m.end(),
               std::back_inserter(v),
               std::tr1::bind(&MyMap::value_type::first, _1));

Вариант 2:

for (MyMap::iterator i = m.begin(); i != m.end(); ++i)
{
    v.push_back(i->first);
}

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

P.S. В процессе написания этого вопроса я пришел к выводу, что лучший способ (для моего проекта) прочитать ключи std :: map - это сохранить их в боковом контейнере и перебрать его. Однако вопрос ремонтопригодности все еще стоит.

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

Ответы 7

Я говорю, давай 2)

Чтобы повысить производительность, вы можете вывести m.end() из цикла и зарезервировать место в векторе.

Не могу дождаться C++ 0x и цикла for на основе диапазона; это сделало бы ваш цикл еще лучше.

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

Ясность всегда лучше ума. Делайте то, что сможете прочитать позже.

Не только вы думаете, что стандартный код немного тупой. Следующий стандарт C++ представит лямбда-функции, чтобы вы могли писать более разборчивый код с помощью стандартных алгоритмов.

Первый такой же читаемый и обслуживаемый, как и второй - если, вы знаете, что делает bind. Я работаю с Boost :: Bind (по сути идентичным std::tr1::bind) достаточно долго, и у меня с ним нет проблем.

Как только TR1 станет частью официального стандарта, вы можете с уверенностью предположить, что любой компетентный программист на C++ поймет его. До тех пор это может вызвать некоторые трудности, но я всегда думаю о долгосрочных перспективах в краткосрочной перспективе.

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

1800 INFORMATION 18.12.2008 03:29

Вы забыли using namespace std::tr1::placeholders: P

Честно говоря, для таких простых алгоритмов последний код, вероятно, легче поддерживать. Но на самом деле я склоняюсь к первому (особенно когда C++ 1x дает нам лямбды!), Потому что он подчеркивает функциональный стиль программирования, который я лично предпочитаю императивному стилю использования цикла.

На самом деле это разные штрихи; стандартные алгоритмы наиболее полезны, когда они сложны или универсальны, но это не так.

Вот как это будет выглядеть с лямбдами:

std::transform(m.begin(), m.end(), std::back_insterter(v),
               [](MyMap::value_type pair){ return pair.first; }
              );

На самом деле, есть другой подход, который я бы предпочел, если бы он не многословен:

using std::tr1::bind;
using std::tr1::placeholders::_1;
std::for_each(m.begin(), m.end(),
              bind(&std::vector<int>::push_back, v,
                   bind(&MyMap::value_type::first, _1)
                  )
             );

И с лямбдами (это, вероятно, по большому счету самый аккуратный и наиболее явный из всех вариантов):

std::for_each(m.begin(), m.end(),
              [&v](MyMap::value_type pair){v.push_back(pair.first);}
             );

Выберите вариант №1, см. Скотт Мейерс, Эффективный STL, пункт № 43, стр. 181.

Но весь смысл этого вопроса в том, что я пытаюсь уравновесить это понятие с пунктом 47 (избегайте кода только для записи).

Michael Kristofik 18.12.2008 06:56

@Kristo, если бы у вас действительно были близкие, практические знания STL + TR1, то вариант №1 был бы для вас так же удобочитаем, как и вариант №2.

paxos1977 19.12.2008 06:12

Когда я вчера посмотрел на ваш вопрос, меня заставила дважды посмотреть не привязка (которую я часто использую), а map :: value_type :: first, которую мне не приходилось использовать очень часто. . Хотя я согласен с тем, что «Ясность всегда превосходит ум», прежде чем ясность, требуется знакомство, и вы не собираетесь знакомиться со стилями, которые вы не используете ...

Я бы также сказал, что, хотя вариант 2 более ясен с точки зрения понимания намеченной цели, он легче скрывает ошибку (любая ошибка в варианте 1 с большей вероятностью будет видна во время компиляции).

Я бы выбрал вариант №3:

#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm_ext/push_back.hpp>

boost::push_back(v, m | boost::adaptors::map_keys);

Это имеет следующие преимущества:

  1. короче

  2. использует именованную функцию для получения ключей

  3. (потенциально) более эффективен (потому что boost::push_back может вызывать reserve() на v)

  4. и не требует резервной пары v.begin(), v.end().

Любой другой путь - чистое безумие.

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