天天看點

淺談lambda表達式-包含其出現緣由

從泛型函數開始介紹,泛型函數引出了lambda表達式。

泛型算法

算法永遠不會執行容器的操作。

隻讀算法

​ find、count這種隻會讀取其輸入範圍内的元素,而從不改變元素的算法,就屬于制度算法。在積累兩個沒用過的隻讀算法。

  • accumulate();

    英文翻譯為積累函數,其實也就是求和函數。可以用來求和。

    accumulate的第三個參數的類型決定了函數中使用哪個加法運算符以及傳回值的類型。

    • int sum = accumulate(vec.cbegin(), vec.cend(), 0);

      最後一個參數是求和起點,也就是sum一開始為0;

    • string sum = accumulate(v.cbegin(), v.cend(), string(""));

      該string初始時為空串,我們通過第三個參數顯示的建立了一個string。下面是錯誤示範

      ❌string sum = accumulate(v.cbegin(), v.cend(), “”);

      錯誤原因:“”被認為是const char*,它是沒有+運算符的,是以便用将會産生編譯錯誤。

  • equal();

    操作兩個序列的算法

    • equal(roster1.cbegin(), roster1.cend(), roster2.cbegin());

      會一個一個比較roster1中標明範圍的元素和在roster2中以roster2.cbegin()開頭的元素。如果所有對應元素都相等,則傳回true,反之傳回false;

    ⭐equal已經預設假定第二個序列至少與第一個序列一樣長,也就是第二個序列長度大于等于第一個序列的長度。

    其實那些隻接受一個單一疊代器來表示第二個序列的算法,都假定了第二個序列至少于第一個序列一樣長。

寫容器元素的算法

積累倆基本的

  • fill();

    隻要傳遞的前兩個參數是有效的範圍就很安全

    fill(vec.begin(), vec.end(), 0);

  • fill_n();

    注意不要越界就好

    三個參數與fill有點差別,fill_n規定了需要寫的個數

    fill_n(vec.begin(), vec.size(), 0);

先稍微記一下一個插入疊代器

  • back_inserter

    暫時兩個例子就行,而且用此疊代器的空容器也可以使用fill-n。

    auto it = back_inserter(v);
    *it = 42;
    for(auto c :v) cout << c <<endl;
               
    fill_n(it, 10, 0);
    for(auto c :v) cout << c <<endl;
               
拷貝算法
  • copy();

    用法類似equal()

    auto ret = copy(begin(a1), end(a1), a2);//把a1的内容拷貝給a2;
               
    同樣的,保證a2長度大于等于a1的長度。
  • replace_copy();

    它之前還有個replace()

    replace(ilst.begin(), ilist.end(), 0, 42);
               

    将序列中所有的0都替換成42;

    replace_copy()用法稍微複雜,仔細觀察例子

    replace_copy(ilist.cbegin(), ilist.end(), back_inserter(ivec), 0, 42);
               
    ivec中包含ilist的一份拷貝,不過原來在ilist中值為0的元素在ivec中都變成42。

重排容器元素的算法

上個例子結束

void elimDups(vector<string> &v){
    sort(v.begin(), v.end());
    auto v_unque = unique(v.begin(), v.end());
    v.erase(v_unque, v.end());
}

int main()
{
    vector <string>v;
    int n;
    cin >> n;
    for(int i=0; i<n; ++i){
        string a;
        cin >> a;
        v.push_back(a);
    }
    elimDups(v);
    for(auto c : v) cout << c << " ";
}
           

向算法傳遞函數

比如sort的第二個版本,可以有第三個參數,這個參數就是謂詞。而這個參數對應了一個自己定義的函數,而且規定了sort的謂詞屬于二進制謂詞,也就是謂詞對應的函數需要兩個參數。但是對于有些算法,比如find_if,接受的是一進制謂詞。但當我們隻給謂詞對應的函數傳一個參數的話是無法滿足的,為了解決這個問題,需要使用另外一些語言特性,比如lambda

lambda表達式

  • 通常形狀

    [capture list](parameter list) -> return type {function body}

    我們可以忽略參數清單和傳回類型,但必須永遠包含捕獲清單和函數體

    auto f = []{return 42;};
    cout << f(); //傳回42
               
    lambda會先根據函數體的代碼推斷出傳回類型,如果函數體隻是一個return語句的話,則傳回類型從傳回的表達式的類型推斷而來。否則傳回類型為void。
  • 注意一下!

    lambda不能有預設參數。

  • 乍一看好像看不出咋用的。。。
    stable_sort(v.begin(), v.end(),
                    [](const string &a, const string &b)
                    { return a.size() < b.size();});
               
使用捕獲清單
  • 一個lambda隻有在其捕獲清單中捕獲一個它所在函數中的局部變量,當然它所在的函數是主函數的話也是很可以的。才能在函數體中使用該變量。

    即 捕獲清單隻用于局部非static變量,lambda是可以直接使用局部static變量和它所在函數之外聲明的名字。

積累倆算法等下要用
  • find_if()
    auto wc = find_if(v.begin(), v.end(), 
                     [sz](const string &a)
                     {return a.size()>=sz;}
                     );
               
    上述可以傳回一個疊代器,指向v中第一個長度大于等于sz的元素。如果不存在,就傳回v.end()的一個拷貝。
  • for_each()
    for_each(v.begin(), v.end(),
            [](const string &a)
            {cout << a;}
            );
               
    功能顯而易見。

習題示例

練習10.15:編寫一個lambda,捕獲它所在函數的int,并接受一個int參數。lambda

應該傳回捕獲的int和int參數的和。

int add(int a){
    auto sum = [a](int b){return a+b;};
    return sum(5);
}
           
練習10.16:使用lambda編寫你自己版本的biggies。
void elimDups(vector<string> &v){
    sort(v.begin(), v.end());
    auto v_unque = unique(v.begin(), v.end());
    v.erase(v_unque, v.end());
}
bool isShorter(const string &a,const string &b){
    return a.size() < b.size();
}
void biggies(vector<string>&words, vector<string>::size_type sz){
    elimDups(words);
    stable_sort(words.begin(), words.end(),
                [](const string &a, const string &b)
                {return a.size() < b.size();});
    auto wc = find_if(words.begin(), words.end(),
                      [sz](const string &a)
                      {return a.size()>=sz;});
    auto count = words.end()-wc;
    cout << count << " " << "word" << (count>1?"s":"") << " of length "<<sz << " or longer"<<endl;
    for_each(wc, words.end(),
             [](const string &a)
             {cout << a << " ";}
             );
    cout << endl;
}
int main()
{
    vector <string>v;
    int n;
    cin >> n;
    for(int i=0; i<n; ++i){
        string a;
        cin >> a;
        v.push_back(a);
    }
    biggies(v, 5);
}