сумма квадрата каждого элемента в векторе, используя for_each - PullRequest
10 голосов
/ 25 августа 2009

Поскольку функция, принятая for_each , принимает только один параметр (элемент вектора), я должен определить где-то static int sum = 0, чтобы к нему можно было получить доступ после вызова for_each. Я думаю, что это неловко. Есть ли лучший способ сделать это (все еще использовать for_each)?

#include <algorithm>
#include <vector>
#include <iostream>

using namespace std;

static int sum = 0;
void add_f(int i )
{
    sum += i * i;

}
void test_using_for_each()
{
    int arr[] = {1,2,3,4};
    vector<int> a (arr ,arr + sizeof(arr)/sizeof(arr[0]));

    for_each( a.begin(),a.end(), add_f);
    cout << "sum of the square of the element is  " << sum << endl;
}

В Ruby мы можем сделать это следующим образом:

sum = 0
[1,2,3,4].each { |i| sum += i*i}   #local variable can be used in the callback function
puts sum    #=> 30

Не могли бы вы показать больше примеров того, как for_each обычно используется в практическом программировании (а не просто распечатывать каждый элемент)? Возможно ли использовать for_each имитировать «шаблон программирования», такой как карта и ввод в Ruby (или карта / складывать в Haskell).

#map in ruby 
>> [1,2,3,4].map  {|i| i*i} 
=> [1, 4, 9, 16]

#inject in ruby 
[1, 4, 9, 16].inject(0)  {|aac ,i| aac +=i}  #=> 30

РЕДАКТИРОВАТЬ: Спасибо всем. Я так много узнал из твоих ответов. У нас так много способов сделать одну и ту же вещь в C ++, что немного усложняет изучение. Но это интересно:)

Ответы [ 6 ]

36 голосов
/ 25 августа 2009

Нет, не используйте std :: collectulate (), используйте std :: inner_product (). Функтор не требуется.

#include <vector>
#include <numeric>

void main()
{
    std::vector <int> v1;
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);

    int x = std::inner_product( v1.begin(), v1.end(), v1.begin(), 0 );
}
21 голосов
/ 25 августа 2009

Использование std :: накапливать

#include <vector>
#include <numeric>

// functor for getting sum of previous result and square of current element
template<typename T>
struct square
{
    T operator()(const T& Left, const T& Right) const
    {   
        return (Left + Right*Right);
    }
};

void main()
{
    std::vector <int> v1;
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);

    int x = std::accumulate( v1.begin(), v1.end(), 0, square<int>() );
    // 0 stands here for initial value to which each element is in turn combined with
    // for our case must be 0.
}

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

7 голосов
/ 25 августа 2009

for_each возвращает (копию) функтор, который он использовал. Итак, как-то так:

#include <algorithm>
#include <vector>
#include <iostream>

template <typename T>
class square_accumulate
{
public:
    square_accumulate(void) :
      _sum(0)
      {
      }

      const T& result(void) const
      {
          return _sum;
      }

      void operator()(const T& val)
      {
          _sum += val * val;
      }

private:
    T _sum;
};

int main(void)
{
    int arr[] = {1,2,3,4};
    std::vector<int> a (arr ,arr + sizeof(arr)/sizeof(arr[0]));

    int sum = std::for_each(a.begin(), a.end(), square_accumulate<int>()).result();

    std::cout << "sum of the square of the element is " << sum << std::endl;
}

Как видно из других ответов, std::accumulate - лучший путь.

4 голосов
/ 25 августа 2009

Не используйте for_each() для этого, используйте accumulate() из заголовка <numeric>:

#include <numeric>
#include <iostream>
using namespace std;

struct accum_sum_of_squares {
    // x contains the sum-of-squares so far, y is the next value.
    int operator()(int x, int y) const {
        return x + y * y;
    }
};

int main(int argc, char **argv) {
    int a[] = { 4, 5, 6, 7 };

    int ssq = accumulate(a, a + sizeof a / sizeof a[0], 0, accum_sum_of_squares());
    cout << ssq << endl;
    return 0;
}

Поведение по умолчанию accumulate() состоит в суммировании элементов, но вы можете предоставить свою собственную функцию или функтор, как мы делаем здесь, и выполняемая операция не обязательно должна быть ассоциативной - 2-й аргумент всегда является следующим элементом прооперирован Эта операция иногда называется reduce на других языках.

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

3 голосов
/ 25 августа 2009

std::for_each предназначен для выполнения действий с каждым элементом. Если вы хотите получить результат из расчета всех элементов, есть std::accumulate. Если вам нужно поведение map на Haskell, используйте std::transform.

Вы можете злоупотребить любым из этих трех, чтобы сделать то же самое, что и любой другой, поскольку в конечном итоге они просто перебирают итератор (за исключением формы transform, которая принимает в качестве входных данных два итератора). Дело в том, что что for_each не является заменой для map / fold - это должно быть сделано с помощью transform / аккумулирования - хотя в C ++ изначально нет чего-то, что выражает концепцию map / fold, как это делает Haskell, - но и gcc, и VC ++ поддерживают OpenMP который имеет гораздо лучший аналог в #pragma omp parallel for.

Inject в Ruby намного ближе к вызову for_each с полноценным функтором, как GMan , описанный выше. Лямбда-функции с захватом переменных в C ++ 0X сделают поведение между двумя языками еще более похожим:

int main(void)
{
    int arr[] = {1,2,3,4};
    std::vector<int> a (arr ,arr + sizeof(arr)/sizeof(arr[0]));

    int sum = 0;
    std::for_each(a.begin(), a.end(), [&](int i) { sum += i*i;} );

    std::cout << "sum of the square of the element is " << sum << std::endl;
}
3 голосов
/ 25 августа 2009

В качестве общего решения такой проблемы с STL: вместо передачи функции вы можете передать functor - например, экземпляр любого класса, реализующего operator(). Это намного лучше, чем полагаться на глобальные переменные, поскольку указанный экземпляр может сохранять и обновлять свое собственное состояние! Вы можете думать об этом как о «типизированной утке во время компиляции»: универсальное программирование не ограничивает вас передачей «функции» в этом месте, всего, что «ведет себя как функция» (то есть имеет правильную operator()) так и сделаю! -)

...