--- tags: books, note --- # Others ## Pair > 用途: 一個有`first`和`second`兩個元素的容器 > 使用時需要引入標頭檔`utility`或`algorithm` 要把兩個元素綁在一起的時候如果嫌麻煩不想自己刻就可以用`pair`代替 `pair`有內建定義用來比較的`less`函數,所以可以直接排序,不用多載`operator`,比較方便一點 ### 用法 #### 宣告 ```cpp pair<first_type, second_type> p; pair<first_type, second_type> p(a, b); ``` - `a`為型態為`first_type`的一變數 - `b`為型態為`second_type`的一變數 #### 存取 ```cpp p.first, p.second; ``` ### 範例 ```cpp= #include<iostream> #include<algorithm> #include<vector> using namespace std; int main() { vector<pair<int, int>> v{{2, 2}, {3, 1}, {2, 1}}; sort(v.begin(), v.end()); for (const auto& i : v) cout << i.first << ' ' << i.second << endl; return 0; } ``` ``` result: 2 1 2 2 3 1 ``` ### make_pair函數 ```cpp pair<first_type, second_type> p = make_pair(a, b); ``` 功能同`pair`的建構元 --- ### ## Range based for loop (since C++11) > 用途: 將iterable的容器整個遍歷 ### 用法 ```cpp for (range_declaration : range_expression) { loop_statement } ``` - `range_declaration`: 為一個變數,其型態要和`range_expression`內元素的型態保持一致或可轉換成一致 - `range_expression`: 為一個可迭代(iterable),或者說具有`begin`跟`end`的容器 - `loop_statement`: 可以為任何敘述、運算式 e.g. ```cpp= vector<int> vec{1, 5, 4, 2, 3}; for (int i : vec) cout << i << ' '; ``` 這個程式碼的作用相等於 ```cpp= vector<int> vec{1, 5, 4, 2, 3}; for (size_t i = 0; i != vec.size(); i++) cout << vec[i] << ' '; ``` 由此可見當使用方式簡單時, 用Range based for loop的程式碼易讀性比較高,寫起來也比較簡潔 --- ## Auto (since C++11) > 用途: 自行判斷變數型態 e.g. ```cpp= vector<pair<int, int>> vec; auto copied_vec = vec; ``` 相等於 ```cpp= vector<pair<int, int>> vec; vector<pair<int, int>> copied_vec = vec; ``` 再看一個極端一點的例子 e.g. ```cpp= template<typename T> void sort(__gnu_cxx::__normal_iterator<T*, vector<T>> l, __gnu_cxx::__normal_iterator<T*, vector<T>> r) { //some code } int main() { vector<int> v{1, 5, 3, 2, 4}; sort(v.begin(), v.end()); return 0; } ``` 等同於 ```cpp= void sort(auto l, auto r) { //some code } int main() { vector<int> v{1, 5, 3, 2, 4}; sort(v.begin(), v.end()); return 0; } ``` --- ## Lambda expression > 又名Anonymous function, 匿名函數 當我們在自訂一些函數(e.g. `sort`)的比較函數時 常常會需要在外部先寫出來再去引用 如果用Lambda expression就可以在**需要用的地方進行定義** lambda函式的形參表比普通函式的形參表多了3條限制: 1. 參數不能有預設值 2. 不能有可變長參數列 3. 不能有無名參數 ### 用法 ```cpp [capture](parameters) mutable exception attribute -> return_type { function_body } ``` 其中```mutable``` ```exception``` ```attribute``` 通常都用不到,可省 ```cpp [capture](parameters) -> return_type { function_body } ``` e.g. ```cpp= [](int x, int y) -> int { return x + y; } // 指定回傳值型態為int ``` 其中```return_type```在回傳值型態明確時可省,變成 ```cpp [capture](parameters) { function_body } ``` e.g. ```cpp= [](int x, int y) { return x + y; } // 從return語句中隱式獲得的返回值類型 [](int& x) { ++x; } // 沒有return語句 -> lambda函數的返回值為void ``` `parameters`在沒有```return_type``` 或 ```mutable```, ```exception```, ```attribute``` 也可省 最簡化版: ```cpp [capture] { function_body } ``` e.g. ```cpp= []() { ++global_x; } // 沒有參數,僅僅是訪問一個全局變量 []{ ++global_x; } // 與前者相同,()可以被省略 ``` - `capture`: 擷取列表,在lambda表達式中使用外部之變數都要經由擷取列表擷取,利用以下形式可以選擇要以值傳遞或是引用(參照)傳遞 - `parameters`: 參數,和一般函式的參數用法一樣,不過不能設初始值,也不能用`auto`代替(C++14以上可以) - `return_type`: 回傳值型態 - `function_body`: 函式主體,可以是任何表達式 #### 擷取列表 ```cpp [] // 沒有定義任何變量,但必須列出空的方括號 [x, &y] // x是按值傳遞,y是按引用傳遞 [&] // 任何被使用到的外部變量都按引用傳入。 [=] // 任何被使用到的外部變量都按值傳入。 [&, x] // x按值傳入。其它變量按引用傳入。 [=, &z] // z按引用傳入。其它變量按值傳入。 ``` ### 實際使用 e.g. ```cpp= inline bool cmp(const int& a, const int& b) { return a > b; } int main() { vector<int> v{1, 5, 4, 2, 3}; sort(v.begin(), v.end(), cmp); return 0; } ``` 等同於 ```cpp= int main() { vector<int> v{1, 5, 4, 2, 3}; sort(v.begin(), v.end(), [](const int& a, const int& b) { return a > b; }); return 0; } ``` --- ## I / O優化 C++的 `cin`/`cout` 相較於C的`printf`/`scanf` 基本上在一般情況下是慢蠻多的 所以需要一些**優化**來補足這個缺點 以下提供常見的優化方式供參考 ### cin.tie + sync_with_stdio 這種最簡單,只要在`int main()`裡一開始放這兩行就能發揮作用 ```cpp cin.tie(nullptr); ios::sync_with_stdio(false); ``` 如果換行很多的時候可以在標頭檔下面加上`#define` i.e. ```cpp= #include <iostream> #define endl '\n' int main() { cin.tie(nullptr); ios::sync_with_stdio(false); return 0; } ``` :::info > 更多的 I/O 優化可以參考 [$9^{th}$進階助教的 blog](https://happylearningcoding.blogspot.com/2020/04/c-io.html#more) > [color=#4fff98][name=9th進階助教$][time=Sat, May 16, 2020 12:24 PM] ::: --- ## limits 有的時候我們會需要用到一些型態的最大最小值 像是`int`的最大$2^{31}-1$ aka $2147483647$之類的 那這時候就可以用`<limits>`裡的內建函式 i.e. ```cpp numeric_limits<target_type>::max() numeric_limits<target_type>::min() ``` - `target_type`: 需要求最大最小值的型態 那如果是字串大小的最大值,一般又會用另一種表示法 i.e. ```cpp string::npos ``` 這個常數代表的是`size_t`的 $-1$ 不過`size_t`是一個無號整數,所以實際上等於$2^{32}-1$ aka $4294967295$ --- ## 排版 #### 原則 - 每行對齊乾淨 - operator 前後加上 space - 逗號前面不用 space, 後面要 space - 大括號後要換行, 之後程式碼要縮排 - 小括號和大括號中間要一個空格 e.g. ```cpp= int main() { int tmp = 0, n; cin >> n; for (int i = 0; i < n; i++) { tmp += i; } cout << tmp << endl; return 0; } ``` 這樣的排版是不是很美觀呢? 舉個例子,$8^{th}$ 副班長,因為沒有排版習慣,去海高的時候被教授唸了一下 :poop: 總之,多看別人的 code 之後,寫的時候就會有排版的習慣了,排版是一個好的習慣,所以 space 不要太過節省哦! :::info > Sublime Text使用者可以參考[$9^{th}$進階教學的codebook](https://hackmd.io/@konchin/sublimeTutorial#附錄1-添加自動排版功能) > [name=9th進階教學][time=Sat, May 16, 2020 12:30 PM] ::: > [name=9th進階助教][time=Sat, May 16, 2020 12:43 PM] --- ## 一些技巧 ### 輸出入by檔案 因為每次在測測資的時候都要一直複製貼上 而且輸出的東西又會跟輸入的東西混在一起 所以可以透過改變I/O stream來方便修改/存取 ![](https://i.imgur.com/co561cF.png) --- ### 語法模板 + debug 懶人必備 ```cpp! #include <bits/stdc++.h> using namespace std; using ll = long long; using pii = pair<int, int>; using pll = pair<ll, ll>; #define F first #define S second #define FOR(i, a) for(ll i = 0; i < a; i++) #define REP(i, a, b) for(auto i = a; i != b; i++) #define ALL(x) x.begin(), x.end() #define GET(i, x) get<i>(x) #define mkp make_pair #define pb push_back #define debug(...) do{\ fprintf(stderr, "%s - %d : (%s) = ", __func__, __LINE__, #__VA_ARGS__);\ _DO(__VA_ARGS__);\ }while(0) template<typename T> void _DO(T&&x){cerr << x << '\n';} template<typename T, typename... A> void _DO(T&&x, A&&...As){cerr << x << ", ";_DO(As...);} int main() { cin.tie(nullptr), cout.tie(nullptr), ios::sync_with_stdio(false); return 0; } ``` > [color=#FF0000][name=9th教學顧問][time=Sun, June 21, 2020 22:14] --- > [color=#2329d1][name=9th進階教學][time=Sat, May 16, 2020 11:57 AM] ---