2009-08-25 7 views
10

for_eachが受け入れる関数は1つのパラメータ(ベクトルの要素)のみをとるため、 を呼び出すとをどこかに定義する必要があります。 。私はこれが厄介だと思う。これを行うには良い方法です(まだfor_eachを使用しています)? Rubyで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; 
} 

、我々はそれをこのように行うことができます。

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/fold)で注入できますか?

#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++で行う方法はたくさんありますが、それは少し難しいものです。しかし、それは面白いです:)

答えて

18

使用std::accumulate

#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をエミュレートすることができ:: nice GMan's answerのように蓄積されますが、私はそれがために設計されたため、使用してのstd ::蓄積は、あなたのコードをより読みやすくなると信じていますそのような目的。より標準的なアルゴリズムhereを見つけることができます。

+1

ベクトルイテレータはstd名前空間にあるとは限りません。それが正しい場合、ADLはここで動作することが保証されておらず、質問者はコンパイラを指定していません。 –

+2

あなたはそうです。私はちょうどチェックした - 標準は、イテレータがstd名前空間の一部であることを保証しません。逆イテレータだけが 'std'名前空間の一部です。 –

+0

ワンワン:うわー、いいキャッチ。私は標準をチェックし、あなたは絶対に正しいです。だからベクトル :: iteratorがT *にtypedefedされた場合、ADLは実際には失敗します。 OPの注目を集めようとしています...(投稿はそれ以外の点では優れています) –

7

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は行くための最良の方法です。

+0

+1、私はそれを考えなかった。 –

+3

これは、std :: accumulateエミュレーションの良いサンプルです。教育目的に役立ちます。 –

+0

投票のコメント?何が間違っているのかわからない場合は改善できません。 – GManNickG

3

STLの問題の一般的な解決策として、関数を渡す代わりにfunctor(たとえば、operator()を実装するすべてのクラスのインスタンス)を渡すことができます。これは、グローバル変数に依存するよりもはるかに優れています。これは、前述のインスタンスが独自の状態を保持し更新できるためです。あなたはそれを「コンパイル時のダックタイピング」の一種と考えることができます:ジェネリックプログラミングは、あなたが "機能のように振る舞う"(すなわち、適切なoperator()を持つ)その場所で "関数"を渡すように制約しません!だけでなく - )

3

<numeric>ヘッダからaccumulate()を使用し、このためfor_each()を使用しないでください:

#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を任意の数値タイプを受け入れるクラステンプレートにすることができます。

32

いいえ、std :: accumulate()を使用しないでください。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); 
} 
+0

非常にエレガントな...まさに私が探していたもの! – Jacob

+3

なぜこれがトップの回答ではないのですか? – math

3

std::for_each要素で何かを行うためです。 のすべての結果がの場合は、std::accumulateです。 Haskellのmapの動作を希望する場合は、std::transformを使用してください。

2つのイテレータを入力として受け取るtransformのフォームを除き、最終的にはイテレータを繰り返し処理するだけなので、他のものと同じことを行うために、これらの3つのいずれかを悪用することができます。 for_eachは、map/foldの代わりではありません。これは、transform/accumulateによって行われるべきです.C++では、マップ/折りたたみコンセプトやHaskellのことをネイティブに表現するものはありませんが、gccとVC++は、 #pragma omp parallel forではずっと良いアナログです。

Rubyで注入すると、前述のGManのような本格的なファンクタを使用してfor_eachを呼び出すと非常によく一致します。 C++ 0Xで可変キャプチャを使用するラムダ関数は、2つの言語間の動作をさらに似ています。

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; 
} 
+1

はsum + = i * iでなければなりません。 – mskfisher

関連する問題