Как Вы используете функции stl как for_each?

Я начал использовать stl контейнеры, потому что они вошли очень удобные, когда я нуждался в функциональности списка, набора и карты и не имел ничто иное в наличии в моей среде программирования. Я не заботился очень об идеях позади него. Документация STL была интересна до такой степени, когда, она пришла к функциям, и т.д. Затем я пропустил чтение и просто использовал контейнеры.

Но вчера, все еще будучи ослабленным от моего отпуска, я просто дал ему попытку и хотел пойти немного больше stl путем. Таким образом, я использовал функцию преобразования (могу я иметь определенные аплодисменты для меня, спасибо).

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

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

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

Спасибо.

PS: Я знаю, что существует реальный for_each цикл в повышении. Но я игнорирую его здесь, так как это - просто удобный способ для моих обычных циклов с итераторами, которые я предполагаю.

6
задан Mike P 29 January 2014 в 11:36
поделиться

8 ответов

Вы найдете разногласия среди экспертов, но я бы сказал, что for_each и transform немного отвлекают. Сила STL в том, чтобы отделить нетривиальные алгоритмы от данных, с которыми работают.

Определенно стоит поэкспериментировать с лямбда-библиотекой Boost, чтобы увидеть, как вы с ней справитесь. Однако, даже если вы найдете синтаксис удовлетворительным, огромное количество задействованного оборудования имеет недостатки с точки зрения времени компиляции и возможности отладки.

Я советую использовать:

for (Range::const_iterator i = r.begin(), end = r.end(); i != end(); ++i)
{
   *out++ = ..   // for transform
}

вместо for_each и преобразовать , но, что более важно, познакомьтесь с алгоритмами, которые очень полезны : ] sort , unique , rotate , чтобы выбрать три наугад.

3
ответ дан 8 December 2019 в 13:44
поделиться

Увеличение счетчика для каждого элемента последовательности не является хорошим примером для for_each .

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

Это код, который я написал сегодня:

// assume some SinkFactory class is defined
// and mapItr is an iterator of a std::map<int,std::vector<SinkFactory*> >

std::for_each(mapItr->second.begin(), mapItr->second.end(),
    checked_delete<SinkFactory>);

checked_delete является частью boost, но реализация тривиальна и выглядит так:

template<typename T>
void checked_delete(T* pointer)
{
    delete pointer;
}

Альтернативой было бы написать следующее:

for(vector<SinkFactory>::iterator pSinkFactory = mapItr->second.begin();
    pSinkFactory != mapItr->second.end(); ++pSinkFactory)
    delete (*pSinkFactory);

More than что, как только у вас есть этот checked_delete , записанный один раз (или если вы уже используете boost), вы можете удалять указатели в любой последовательности где угодно, с тем же кодом, не заботясь о том, какие типы вы повторяете (то есть , вам не нужно объявлять vector :: iterator pSinkFactory ).

Также есть небольшое улучшение производительности из-за того, что с for_each контейнер .end () будет вызываться только один раз, и потенциально значительное улучшение производительности зависит от реализации for_each (это может быть реализовано по-разному в зависимости от полученного тега итератора).

Кроме того, если вы комбинируете boost :: bind с алгоритмами последовательности stl, вы можете делать всевозможные забавные вещи (см. Здесь: http://www.boost.org/doc/libs/1_43_0/libs/bind /bind.html#with_algorithms).

2
ответ дан 8 December 2019 в 13:44
поделиться

Локальные классы - отличное средство для решения этой проблемы. Например:

void IncreaseVector(std::vector<int>& v)
{
 class Increment
 {
 public:
  int operator()(int& i)
  {
   return ++i;
  }
 };

 std::for_each(v.begin(), v.end(), Increment());
}

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

1
ответ дан 8 December 2019 в 13:44
поделиться

Это действительно реальные проблемы, и они решаются в следующей версии стандарта C ++ («C ++ 0x»), которая должна быть опубликована либо в конце этого года, либо в 2011 году. Эта версия C ++ вводит понятие под названием Лямбда-выражения C ++ , которые позволяют создавать простые анонимные функции внутри другой функции, что позволяет очень легко выполнять то, что вы хотите, не разбивая код на крошечные кусочки. Лямбды поддерживаются (экспериментально?) В GCC начиная с GCC 4.5.

0
ответ дан 8 December 2019 в 13:44
поделиться

Я считаю его наиболее полезным при использовании вместе с boost :: bind и boost :: lambda , так что мне не нужно писать собственный функтор. Это всего лишь крошечный пример:

class A
{
public:
    A() : m_n(0)
    {
    }

    void set(int n)
    {
        m_n = n;
    }

private:
    int m_n;
};

int main(){    

    using namespace boost::lambda;

    std::vector<A> a;
    a.push_back(A());
    a.push_back(A());

    std::for_each(a.begin(), a.end(), bind(&A::set, _1, 5));


    return 0;
}
3
ответ дан 8 December 2019 в 13:44
поделиться

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

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

std::for_each(vec.begin(), vec.end(), [&](int& val){val++;})

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

Выполнение описанного выше сегодня будет выглядеть так:

int incr(int& val) { return val+1}

// and at the call-site
std::for_each(vec.begin(), vec.end(), incr);

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

  • какая операция выполняется (если она названа соответствующим образом)
  • , на какие элементы влияют

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

7
ответ дан 8 December 2019 в 13:44
поделиться

Думаю, сообщество C ++ обеспокоено теми же проблемами. В новом стандарте C ++ 0x, подлежащем проверке, вводятся лямбда-выражения. Эта новая функция позволит вам использовать алгоритм при написании простых вспомогательных функций непосредственно в списке параметров алгоритма.

std::transform(in.begin(), int.end(), out.begin(), [](int a) { return ++a; })
1
ответ дан 8 December 2019 в 13:44
поделиться

Такие библиотеки, как STL и Boost , сложны еще и потому, что они должны решать все задачи и работать с любой формой пластины.

Как пользователь этих библиотек - вы не планируете переделывать .NET? - вы можете использовать их упрощенные лакомства.

Вот, возможно, более простой вариант foreach из Boost, который мне нравится использовать:

BOOST_FOREACH(string& item in my_list)
{
    ...
}

Выглядит намного аккуратнее и проще , чем использование .begin () , . end () и т. д., но он работает практически для любой итерируемой коллекции (не только для массивов / векторов).

0
ответ дан 8 December 2019 в 13:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: