###学習《C++Primer》-3

6979 ワード

クリックしてEvernoteのテキストを表示 .
#@author:       gr
#@date:         2014-10-04
#@email:        [email protected]

Part 3:STL汎用アルゴリズム(第10章)
一、アルゴリズムは永遠に容器の操作を実行しない
アルゴリズム自体はコンテナの操作を実行せず、反復器でのみ実行され、反復器の操作を実行し、反復器を使用してアルゴリズムをより汎用化します.アルゴリズムのプログラミング仮定:アルゴリズムは決して下層容器の大きさを変えない.アルゴリズムは、要素の値を変更したり、要素を移動したりすることができますが、直接追加したり削除したりすることはありません.inserterの反復器もあり、容器に要素を追加することができます.
二、読み取り専用アルゴリズムaccumulateは、加算動作を行い、数値加算を行ってもよいし、文字列加算(接続)を行ってもよい.
accumulate(c.cbegin(), c.cend(), string(""));
equalは、2つの容器の要素が一致しているかどうかを比較し、最初の2つは容器の範囲を表し、3番目は2番目の容器の最初の位置を表す.2番目のシーケンスが少なくとも1番目のシーケンスと同じ長さであることを保証します.
equal(c1.cbegin(), c1.cend(), c2.cbegin());

三、容器要素を書くアルゴリズムfillfill_nは容器の大きさを変えないので、十分なスペースが確保されています.
vector<int> vec(10);
fill(vec.begin(), vec.end(), 0);        //fill(begin, end, val);
fill_n(vec.begin(), vec.size(), 0);     //fill_n(pos, n, val);
vector<int> vec2;
fill_n(vec2.begin(), 10, 0);            //  ,vec2   ,    10   
back_inserterは挿入反復器であり、この反復器によって付与されると、push_back関数が自動的に呼び出されます.
vector<int> vec;
auto it = back_inserter(vec);
//    ,     push_back    
*it = 42;
fill_n(back_inserter(vec), 10, 1);
copyは、エレメントを宛先に書き込みます.
copy(vec1.cbegin(), vec2.cend(), back_inserter(vec2));
replaceは、要素値を置き換えます.
// 0   42
replace(vec.begin(), vec.end(), 0, 42);
replace_copyは、第3の反復器を受け入れ、調整後のシーケンスの保存位置を示す.
replace_copy(vec.cbegin(), vec.cend(), back_inserter(vec2), 0, 42);

四、容器要素を並べ替えるアルゴリズムuniqueは、重複する要素を本当に削除していません.重複する要素を最後に並べます.end()メンバー関数が返す反復器の位置は変わりません.eraseを呼び出して削除した後、重複する要素を本当に削除します.
vector<string> words = {"fox", "the", "red", "jump"}
//  
sort(words.begin(), words.end());
//        ,                 
auto end_unique = unique(words.begin(), words.end());
//      
words.erase(end_unique, words.end());

五、lambda式
$$$lambda$$式は関数と同様にアルゴリズム呼び出しに渡すことができます.一般的な形式は次のとおりです.
[capture list] (parameter list) -> return type { function body }

パラメータリストと戻りタイプは無視できますが、キャプチャリストと関数体は常に含まなければなりません.
auto f = [] {return 42;} ;
//        
[](const string& a, const string& b){ return a.size() < b.size(); }

次に、このlambda式を用いてアルゴリズムに伝達してソートする.
stable_sort(words.begin(), words.end(), 
            [](const string& a, const string& b)
            { return a.size() < b.size(); } );

取得リストを使用して現在のローカル変数を取得し、lambda式で直接使用できます.
[sz](const string& a){
    return a.size() >= sz;
};

六、変数の修正がlambda式に与える影響
取得のタイプ:1.値の取得;2.参照取得;3.暗黙的なキャプチャ.≪値の取得|Value Capture|emdw≫:取得された値はlambdaの作成時にコピーされ、取得されたローカル変数の値を変更してもlambdaの値には影響しません.値取得:参照取得は値取得とは逆に、変更した値がlambda式の内部に作用します.
size_t v1 =42;
auto f1 = [v1]{return v1;}      //   
auto f2 = [&v1]{return v1;}     //    
v1 = 0;
auto j = f1();                  //   ,   42
auto k = f2();                  //    ,   0

暗黙的なキャプチャ:コンパイラにlambda式のコードに基づいて、どの変数を使用する必要があるかを推定させます.暗黙的なキャプチャであることを示すために、キャプチャリストに&または=を追加します.&参照取得を表します.=値取得を表します.
wc = find_if(words.begin(), words.end(), 
                [=](const string& s){return s.size > sz;} );

七、lambda式で変数値を修正する
一般的にlambda式では、値取得でコピーされた変数の値は変更されません.lambda式で値を変更する場合は、式をmutableとして宣言する必要があります.
size_t v1 = 42;
auto f = [v1] () mutable {return ++v1;};
v1 = 0;
auto j = f();       //j 43

リファレンスキャプチャが変更できるかどうかは、リファレンスがconstタイプであるかどうかによって異なります.
size_t v1 = 42;
auto f = [&v1]{return ++v1;};   //v1  const
v1 = 0;
auto j = f2();      //j 1

八、lambda式の戻りタイプ
条件演算子のタイプに基づいて推定できるため、いくつかの式では戻りタイプを指定する必要はありません.lambda式の戻りタイプを定義する必要がある場合は、末尾の戻りタイプを使用する必要があります.
transform(vi.begin(), vi.end(), vi.begin(),
            [](int i)-> int                     //       int
            { if (i < 0) return -i; else return i; });

九、パラメータバインドbindの一般的な形式:
auto newCallable = bind(callable, arg_list);
arg_listのパラメータには、次のような形が含まれる場合があります.nの名前、nは整数で、これらのパラメータは“ポイント記号”で、いくつかのパラメータを表します:1は第1のパラメータを表して、2は第2のパラメータを表します.
find_if(words.begin(), words.end(),
        bind(check_size, _1, sz));          //         sz
        

パラメータ:
auto g = bind(f, a, b, _2, c, _1);          //g              
g(X, Y);                                    //  g(X,Y)   f(a, b, Y, c, X);

パラメータの再配置:
auto g = bind(f, _2, _1);
g(X, Y);                                    //  g(X,Y)   f(Y, X),      

十、反復器を挿入する
挿入反復器に値を割り当てるのはコンテナに書き込むのと同じで、挿入関数が自動的に呼び出されます.inserterback_inserterfront_inserterもすべて反復器で、これらの名前は元の反復器の別名で、元の名前は長すぎますo_0!.
vector<int> vi = {1, 2, 3};
auto it = back_inserter(vi);        //     
*it = 4;                            //vi    {1, 2, 3, 4}

十一、フロー反復器
  `iostream`    ,         IO        ,`istream_iterator`,`ostream_iterator`。

istream_iterator<int> in(cin), eof;
int sum = accumulate(in, eof, 0);
ostream_iterator<int> out_iter(cout, "
"); // *out_iter = sum;

十二、逆反復器
逆反復器は、最後に現れる要素を検索する際に非常に有効ですが、逆反復器を直接使用して出力すると、順序が逆になり、反復器のbase()メンバー関数によって正常な反復器に変えることができます.
auto rcomma = find(line.crbegin(), line.crend(), ',');      //      
cout << string(line.crbegin(), recomma) << endl;            //    ,       
cout << string(rcomma.base(), line.cend()) << endl;          //  base()       

十三、アルゴリズムパラメータモード
一般的なアルゴリズムには基本的なパラメータ形式があり,これらのモードを理解し,アルゴリズムの操作にもっと集中することができる.一般的には、次の4つの形式があります.
alg(begin, end, args);
alg(begin, end, dest, args);
alg(begin, end, begin2, args);
alg(begin, end, begin2, end2, args);

十四、アルゴリズム命名規範
  • いくつかのアルゴリズムは、述語
    //unique   
    unique(begin, end);
    unique(begin, end, comp);       //        ,     < ==
  • をリロードするように伝達する.
  • _ifバージョンのアルゴリズム
    //          ,         
    find(c.begin(), c.end(), val);
    find_if(c.begin(), c.end(), condi);      //  condition     
  • コピー要素バージョンと非コピーバージョンを区別する
    reverse(begin, end);                    //           
    reverse_copy(begin, end, dst);          //     dst,      inserter