{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[{"file_id":"1AB3RNlLfkZBSZKu6_Wh20CkIpbxfJIve","timestamp":1663461789208}],"collapsed_sections":[],"authorship_tag":"ABX9TyMiC8qx4QXmuQz40K84olOw"},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"},"gpuClass":"premium"},"cells":[{"cell_type":"markdown","source":["#計算機程式設計二\n","#第九週上課內容\n","# C++ What's New\n","\n","### 主題: C++ 簡介\n","\n"],"metadata":{"id":"9xoK4OIjIfZA"}},{"cell_type":"markdown","source":["###參考資料:\n","- A Tour of C++,  by Bjarn Stroustrup\n","\n"," http://www.stroustrup.com/Tour.html\n","\n"],"metadata":{"id":"05G1Fqg6GRZX"}},{"cell_type":"markdown","source":["這個單元的主要任務介紹 C++，讓大家能夠開始練習用 C++ 寫程式。C++涵蓋的內容非常廣，我們這學期剩下的時間只能認識到其中的一小部分。但是，我們也沒必要把全部的東西都學過才能寫程式，學習程式設計是一個漸進的過程( http://norvig.com/21-days.html) ，應該一邊學一邊寫，而且要培養自學能力。有些瑣碎的東西如果都在課堂上講解，其實聽起來也會很枯燥。我們還是會照著這學期的課程規劃，試著把資訊系統導論和程式設計兩門課結合起來，透過寫程式來認識資工的基本知識，而學習C++只是其中的一部分。另一方面 C++ 持續地演變，從 C++ 11 到 C++ 14 再到 C++ 17 和 C++20 ，增加了不少新的 features，要全面熟悉也必須靠不斷地實作、應用。\n","\n","因為我們已經學過一個學期的 C，所以我們就先以 “What’s New?”做為主軸，來認識 C++。\n","這個標題除了有 “比較 C ++ 和 C 的不同之處” 的意涵之外，也想點出 C++ 最近幾年有甚麼進展和演變。我們還是都採取舉例說明的方式來介紹，介紹的內容會比較片面，不足的地方要自行靠 Google 補足。(譬如我們不需要像剛學 C 的時候，連 `printf` 的各種格式如何使用都要介紹。)\n"],"metadata":{"id":"azEB13vJtHte"}},{"cell_type":"markdown","source":["第一件事還是 Hello, World! ，在 C++ 是這樣寫：\n","可以看到不同的地方是 `#include<stdio.h>` 變成了\n","`#include <iostream>`，然後是用 `std::cout` 來輸出。\n","`std::` 是用來指定 `cout` 的 `namespace` 屬於 standard library，這樣的用意是避免同名的衝突。\n","如果在程式碼前面寫 `using namespace std;` 則之後使用 standard library namespace 底下的東西時，就可以省略開頭的 `std::`。"],"metadata":{"id":"kkghyhylNKIP"}},{"cell_type":"code","source":["%%writefile hello.cpp\n","\n","#include <iostream>\n","int main()\n","{\n","    int x;\n","    std::cout << \"Hello, World!\\n\" << \"...\";\n","    std::cin >> x;\n","    std::cout << \"x = \" << x << '\\n';\n","}\n","\n","/*\n","#include <iostream>\n","using namespace std;\n","int main()\n","{\n","    cout << \"Hello, World!\\n\";\n","}\n","*/"],"metadata":{"id":"ueZaPjLItKXw","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1667904911672,"user_tz":-480,"elapsed":882,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"5e575c98-f98b-4bf6-df34-f8bdac169aa1"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting hello.cpp\n"]}]},{"cell_type":"code","source":["%%shell\n","g++ hello.cpp -std=c++1z -o hello\n","./hello"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"Q2IIYAclOCxr","executionInfo":{"status":"ok","timestamp":1667904921400,"user_tz":-480,"elapsed":6330,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"ae0d2676-3aa3-4230-8f5d-61896aafa5a3"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["Hello, World!\n","...5\n","x = 5\n"]},{"output_type":"execute_result","data":{"text/plain":[]},"metadata":{},"execution_count":5}]},{"cell_type":"markdown","source":["### `string`\n","在 C++ 裡面，有 `string` 資料型態可用 \n","(屬於 standard library 所以可以視為內建)，複製 `string` 比以前在寫 C 程式時簡單很多，用 `+` 就行了。"],"metadata":{"id":"vDbcAirdZzO2"}},{"cell_type":"code","source":["%%writefile E09_01.cpp\n","#include <iostream>\n","#include <string>\n","using namespace std;\n","int main()\n","{\n","    string name, ans;\n","    cout << \"Hi! My name is Hal. \" << \"What's your name?\\n\";\n","    cin >> name;\n","    ans = \"Nice to meet you, \" + name + \".\";\n","    cout << ans << endl;\n","    cout << \"Your name has \" << name.length() << \" characters.\" << endl;\n","    name.clear();\n","    if (name.empty()) {\n","      cout << \"No name.\\n\";\n","    }\n","}"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"jbAgUj0HONZ1","executionInfo":{"status":"ok","timestamp":1667905518093,"user_tz":-480,"elapsed":780,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"0d327f84-69ff-46f4-f6dc-218b1ac9be66"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting E09_01.cpp\n"]}]},{"cell_type":"code","source":["%%shell\n","g++ E09_01.cpp -std=c++1z -o E09_01\n","./E09_01"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"15HoZpdK5iKH","executionInfo":{"status":"ok","timestamp":1667905536744,"user_tz":-480,"elapsed":15242,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"3abcd1f5-23f8-4440-bf69-6653012f12ee"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["Hi! My name is Hal. What's your name?\n","Satch\n","Nice to meet you, Satch.\n","Your name has 5 characters.\n","No name.\n"]},{"output_type":"execute_result","data":{"text/plain":[]},"metadata":{},"execution_count":11}]},{"cell_type":"markdown","source":["### `auto`\n","C++ 可以將變數的型別設為 `auto`，編譯器會替你做 auto type inference，依照前後文決定變數的型別，例如 \n","```\n","auto i = 5;\n","``` \n","或是 \n","```\n","auto x = 2.3; \n","```\n","可以知道 `i` 的型別應該就是 `int` 而 `x` 的型別會是 `double`。\n","\n","### `Range-based for loop`\n","請看底下的範例。\n","`vector` 是 standard library 裡面定的 container， \n","```\n","vector<int> v;\n","```\n","表示 `v` 是由 `int` 構成的 `vector`，每個元素的型別都是 `int`。同理 \n","```\n","vector<string> vs;\n","```\n","則每個元素都是 `string`。在 C++ 可以用以前的 C for loop 語法，但除此之外，C++ 也提供 range-based for loop，請看範例中 `for` 的用法，會循序取出 `vector` 裡面的每個元素。這樣的寫法搭配 `auto` 變數，可以當成片語來使用，比起以往在 C 語言中必須用額外的 index  存取 array 元素的寫法，來得簡潔許多，也不必擔心 index 超出陣列範圍。"],"metadata":{"id":"0DOOrpbQQcyW"}},{"cell_type":"code","source":["%%writefile E09_02.cpp\n","#include <iostream>\n","#include <vector>\n","#include <iomanip>\n","using namespace std;\n","int main()\n","{\n","    vector<int> v{1,2,3,4,5};\n","    cout << v[2] << \"\\n\";\n","\n","    for (const auto& i : v)\n","        cout << setw(3) << i;\n","    cout << endl;\n","\n","    vector<string> vs{\"Vai\", \"Satch\", \"Johnson\"};\n","    for (const auto& s : vs)\n","        cout << s << \"\\n\";\n","}"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"I8RuPjgn3WOn","executionInfo":{"status":"ok","timestamp":1667906595803,"user_tz":-480,"elapsed":473,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"9500bf22-0fcf-4d13-9f18-cf2279af9b95"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting E09_02.cpp\n"]}]},{"cell_type":"code","source":["%%shell\n","g++ E09_02.cpp -std=c++1z -o E09_02\n","./E09_02"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"WdJ1e7hyRha6","executionInfo":{"status":"ok","timestamp":1667906601019,"user_tz":-480,"elapsed":947,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"815a6501-0dbc-40a2-897b-d0a87242b2a5"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["3\n","  1  2  3  4  5\n","Vai\n","Satch\n","Johnson\n"]},{"output_type":"execute_result","data":{"text/plain":[]},"metadata":{},"execution_count":29}]},{"cell_type":"code","source":["%%writefile E09_03.cpp\n","#include <iostream>\n","#include <vector>\n","using namespace std;\n","int main()\n","{\n","    vector<string> vs{\"Tom\", \"John\", \"Amy\", \"Cathy\"};\n","    \n","    for (auto n : vs)\n","        cout << n << endl;\n","}\n"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"onqyDQPYRoqP","executionInfo":{"status":"ok","timestamp":1667827687470,"user_tz":-480,"elapsed":618,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"f41e2d7c-4315-479b-93aa-a6bddc22a87e"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["Writing E09_03.cpp\n"]}]},{"cell_type":"code","source":["%%shell\n","g++ E09_03.cpp -std=c++1z -o E09_03\n","./E09_03"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"OzLM4dkrRuf6","executionInfo":{"status":"ok","timestamp":1667827699373,"user_tz":-480,"elapsed":769,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"7d24aae1-dcba-495a-f129-28f151739563"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["Tom\n","John\n","Amy\n","Cathy\n"]},{"output_type":"execute_result","data":{"text/plain":[]},"metadata":{},"execution_count":11}]},{"cell_type":"markdown","source":["### `Reference &`\n","C++ 多了一種變數類型，叫做 reference。通常是用在函數傳遞參數，可以達到 call-by-reference 的效果。回憶一下，在 C 語言裡面的函數呼叫，都是 call-by-value，函數呼叫時傳遞的其實是參數的值 (將值放進 Stack)，所以如果在函數中對參數做修改，不會影響到原本的變數 (底下 swap 的例子)。也因為如此，C 必須利用指標變數，傳遞記憶體位址，來達到 call-by-reference 的效果。在 C++ 則是直接提供 能夠 達到 call-by-reference 的機制。對照底下兩個範例的差異。\n","\n","\n"],"metadata":{"id":"mviKuWaapY32"}},{"cell_type":"code","source":["%%writefile E09_04.cpp\n","#include <iostream>\n","using namespace std;\n","void swap1(int i, int j)\n","{\n","    int t = i;\n","    i = j;\n","    j = t;\n","}\n","void swap0(int *pi, int *pj)\n","{\n","    int t = *pi;\n","    *pi = *pj;\n","    *pj = t;\n","}\n","\n","void swap2(int& i, int& j)\n","{\n","    int t = i;\n","    i = j;\n","    j = t;\n","}\n","\n","int main()\n","{\n","    auto x = 5, y = 7;\n","    cout << \"(x, y) = (\" << x << \", \" << y << \")\\n\";\n","    swap1(x, y);\n","    cout << \"swap1: (\" << x << \", \" << y << \")\\n\";\n","\n","    swap0(&x, &y);\n","    \n","    swap2(x, y);\n","    cout << \"swap2: (\" << x << \", \" << y << \")\\n\";\n","\n","}\n"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"IQmV7xfGSEjN","executionInfo":{"status":"ok","timestamp":1667827998677,"user_tz":-480,"elapsed":313,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"b403c4e7-0d83-4953-b19a-a5410c38fec3"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting E09_04.cpp\n"]}]},{"cell_type":"code","source":["%%shell\n","g++ E09_04.cpp -std=c++1z -o E09_04\n","./E09_04"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"UNS9-aKBSRh9","executionInfo":{"status":"ok","timestamp":1667827972555,"user_tz":-480,"elapsed":315,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"067f9391-6149-434c-ff30-e3963342b0a1"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["(x, y) = (5,7)\n","swap1: (5,7)\n","swap2: (7,5)\n"]},{"output_type":"execute_result","data":{"text/plain":[]},"metadata":{},"execution_count":20}]},{"cell_type":"markdown","source":["差別只是參數的型別從 `int` 變成 `int &`。Reference 相當於是替變數取了一個代稱，所以在 `swap2` 函數裡面的 `i` 其實就相當於是 main 裡面的 `x`，而 `j` 則相當於 `y`。\n","除了參數，回傳值也可以用 Reference。底下的程式碼，註解裡面的寫法是錯的。原因是當函數結束之後，函數內原本記錄在 Stack 中的局部變數都不能再被使用，所以把局部變數的 reference 傳回去是沒有意義的，會造成程式錯誤。此外，如果不希望傳回來的 Reference 被修改，可以把函數改成 \n","```\n","const int& max(int& i, int& j);\n","```\n","，這樣一來如果使用 \n","```\n","max(x,y)=0;\n","```\n","，在編譯時編譯器就會提出錯誤訊息讓程式設計者知道用法不對。"],"metadata":{"id":"gxI_Kd-iTROY"}},{"cell_type":"code","source":["%%writefile E09_05.cpp\n","\n","#include <iostream>\n","using namespace std;\n","int& max(int& i, int& j)\n","{\n","    return (i>j) ? i : j;\n","}\n","int main()\n","{\n","    auto x = 5, y = 7;\n","    cout << x << \" \" << y << \"\\n\";\n","    max(x, y) = 0;\n","    cout << x << \" \" << y << \"\\n\";\n","}\n","\n","\n","/*\n","// WRONG!\n","#include <iostream>\n","using namespace std;\n","int& max(int& i, int& j)\n","{\n","    int m;\n","    m = (i>j) ? i : j;\n","    return m;\n","}\n","int main()\n","{\n","    auto x = 5, y = 7;\n","    cout << x << \" \" << y << \"\\n\";\n","    max(x, y) = 0;\n","    cout << x << \" \" << y << \"\\n\";\n","}\n","*/"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"baLogo0RTpb8","executionInfo":{"status":"ok","timestamp":1667908256982,"user_tz":-480,"elapsed":289,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"f2a3ca89-1ee3-4e7b-ef6f-e1549980255c"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting E09_05.cpp\n"]}]},{"cell_type":"code","source":["%%shell\n","g++ E09_05.cpp -std=c++1z -o E09_05\n","./E09_05"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"MvDHof4ASRWw","executionInfo":{"status":"ok","timestamp":1667908262866,"user_tz":-480,"elapsed":1376,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"f4541ccd-63a7-4541-cace-b686c48f41e8"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["5 7\n","5 0\n"]},{"output_type":"execute_result","data":{"text/plain":[]},"metadata":{},"execution_count":39}]},{"cell_type":"markdown","source":["### `Lambda functions`\n","C++11 將 lambda functions 納入標準中，所以 C++ 也可以寫出沒有名字的函數，讓某些應用變得方便很多。先看下面的例子：\n"],"metadata":{"id":"qjAlxttObaIa"}},{"cell_type":"code","source":["%%writefile E09_06.cpp\n","// -std=c++11  or   -std=c++14   or   -std=c++1z\n","#include <algorithm>\n","#include <iostream>\n","#include <vector>\n","#include <iomanip>\n","using namespace std;\n","int main()\n","{\n","    vector<int> v;\n","    for (int i = 1; i < 10; ++i) v.push_back(i*5);\n","    for (const auto& i : v) cout << setw(5) << i;\n","    cout << endl;\n","\n","    for_each(v.begin(), v.end(), [] (int n) {\n","        if (n % 2 == 0) {\n","            cout << n <<\" is even \" << endl;\n","        }\n","    });\n","    \n","\n","    for_each(v.begin(), v.end(), \n","      [] (int& n) {\n","          n += 7;\n","      }\n","    );\n","    for (const auto& i : v) cout << setw(5) << i;\n","    cout << endl;\n","\n","    for (const auto & y : v) cout << setw(5) << setfill('#') << y;\n","    cout << endl;\n","    for (auto & y : v) y = y+1;\n","    for (auto y : v) cout << setw(5) << setfill('*') << y;\n","    cout << endl;\n","    \n","}"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"WT37_-HUWWyY","executionInfo":{"status":"ok","timestamp":1667909345744,"user_tz":-480,"elapsed":304,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"ae77b1a9-848c-4f18-f602-082f7c8e12c8"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting E09_06.cpp\n"]}]},{"cell_type":"code","source":["%%shell\n","g++ E09_06.cpp -std=c++1z -o E09_06\n","./E09_06"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"6E0BIylxWiIu","executionInfo":{"status":"ok","timestamp":1667909357470,"user_tz":-480,"elapsed":318,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"57ed204d-abe0-4aee-cae1-f68c1f13c41a"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["    5   10   15   20   25   30   35   40   45\n","10 is even \n","20 is even \n","30 is even \n","40 is even \n","   12   17   22   27   32   37   42   47   52\n","###12###17###22###27###32###37###42###47###52\n","***13***18***23***28***33***38***43***48***53\n"]},{"output_type":"execute_result","data":{"text/plain":[]},"metadata":{},"execution_count":56}]},{"cell_type":"markdown","source":["首先， `#include` 多了好幾個 header 需要引入，其中 `<algorithm>` 是為了後面要用到的 `for_each`，`<iomanip>` 則是為了設定 `cout` 格式。 程式碼一開始的 `for` 利用 `vector` 的 `push_back` 函數把資料放入 `v` 裡面，之後再把 `v` 的內容一一顯示出來，其中 `setw(5)`是 `<iomanip>` 提供的函數，作用是將 `cout` 的輸出格式設定為寬度等於 `5`，所以顯示出來的每筆數字都會占 5 個字元，不足的會補上空白。接下來的 `for_each` 函數，用來將 `vector` 的每個元素，一一代入指定的函數中。`for_each` 的前兩個參數用來指定 `vector` 需要被處理的範圍，分別代表開始和結束的位置。`for_each` 的第三個參數則是要被代入的函數，在範例中就是用 lambda function 的方式，直接定義要被代入的函數，語法如下，看起來和一般的函數沒甚麼不同，只是不需要取名字，只要寫 `[]` 就行了\n","```\n","    [] (int n) {\n","        if (n % 2 == 0) {\n","            cout << n <<\" is even \" << endl;\n","        }\n","    }\n","```\n","\n","### More Lambda"],"metadata":{"id":"U05RDiHLWv6f"}},{"cell_type":"code","source":["%%writefile E09_07.cpp\n","#include <iostream>\n","#include <functional>\n","#include <math.h>\n","#include <iomanip>\n","#define TOL 0.00001\n","using namespace std;\n","void print(function<double(double)> f, double x)\n","{\n","    cout << \"f(\" << fixed << setprecision( 5 )  << x << \") = \" << f(x) << endl;\n","}\n","void print(double x)\n","{\n","    cout << \"ans = \" << x << endl;\n","}\n","double fixed_point(function<double(double)> f, double guess)\n","{\n","    auto close_enough = [](double v1, double v2)\n","        { return fabs(v1-v2)<TOL; } ;\n","\n","    auto next_guess = f(guess);\n","    if (close_enough(next_guess, guess)) return next_guess;\n","    else return fixed_point(f, next_guess);\n","}\n","\n","double mysqrt(double x)\n","{\n","    return fixed_point(\n","      [x](double y){return (y+(x / y))/2.0; }\n","      , \n","      1.0);\n","}\n","\n","double mycbrt(double x)\n","{\n","    return fixed_point([x](double y){return (y+(x / (y*y)))/2.0; }, 1.0);\n","}\n","int main()\n","{\n","    auto x = 2.0;\n","    print(mysqrt, x);\n","    print(mycbrt(5.0));\n","}"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"9h9oBGmhXPPe","executionInfo":{"status":"ok","timestamp":1667910566071,"user_tz":-480,"elapsed":1292,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"0ff4fb8d-db28-470d-9853-cd5041a7f6d1"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting E09_07.cpp\n"]}]},{"cell_type":"code","source":["%%shell\n","g++ E09_07.cpp -std=c++1z -o E09_07\n","./E09_07"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"WbEGVnOzXYJA","executionInfo":{"status":"ok","timestamp":1667910050269,"user_tz":-480,"elapsed":1153,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"ba1131b6-e5b6-4c91-f24b-b2704b6ed28b"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["f(2.00000) = 1.41421\n","ans = 1.70997\n"]},{"output_type":"execute_result","data":{"text/plain":[]},"metadata":{},"execution_count":58}]},{"cell_type":"markdown","source":["### Classes\n","\n","C++ 和 C 最大的差別，應該就是多了 class，讓使用者能夠自訂型別並且運用物件導向的概念來設計程式。 我們底下介紹的是最經典的範例之一，程式碼取自 A Tour of C++ 這本書。\n","\n","C++  Standard Library 裡面已經有提供  complex 的型別，我們用的範例是它的簡化版。在其他C++的書裡面也可以找到類似的版本。\n","\n","class 的語法有點類似 C 語言裡面的 struct，但是在 C++ 裡面我們自己定義的 class 直接就能拿來當作新的型別來使用，例如\n","```\n","class complex \n","{\n"," ...\n","} ;\n","```\n","\n","這樣就定出了一個新的型別叫做 `complex`，要注意最後面波浪括號之後還有一個分號。我們用這個新的型別來宣告變數，例如\n","\n","```\n","complex z;\n","```\n","這樣 `z` 就是一個 `complex` 變數，通常我們稱它為 物件。\n","C++ Class  的特色是把資料和函數綁在一起，`class` 裡面所定義的成員可以包含資料和函數，讓物件導向程式設計的概念得以實現。Class 不只是把資料包裝起來，通常也會把處理資料所需的函數包含進來，譬如前幾頁的例子裏面的 `name.size()` 或是 `v.push_back(i*5)`，其中 `name` 是物件，`size()` 是它的成員函數，`v` 是物件，`push_back()` 是成員函數。\n","\n","C++ class 的成員預設都是 `private`，意思是無法從 `class` 外部存取，只有那個 `class` 自己的成員可以使用 `private`  區域的資料或是函數。如果要開放給外部使用，則要在 `class` 裡面另外再定一個區塊叫做 `public:`，寫在 `public` 區塊的成員資料或是成員函數，可以被 `class` 以外的函數或是其他物件存取。放在 `public` 區塊裡的函數，提供了 `class` 和外界溝通的管道。\n","\n","下面的範例涵蓋了 C++ class 的基本定義方式，裡面有許多細節我們會在接下來幾周逐一介紹。剛開始的時候，可以先試著依樣畫葫蘆，自己試著定義其他的 `class`，譬如模仿 `complex` ，定義出有理數 `rational` 型別，(`complex` 有實部和虛部，有理數則是有分子和分母)，這會是很好的練習，對於建立物件導向的概念以及熟悉語法會有些幫助。"],"metadata":{"id":"7GIcibNngpD-"}},{"cell_type":"code","source":["%%writefile E09_08.cpp\n","#include <iostream>\n","#include <vector>\n","#include <iomanip>\n","using namespace std;\n","\n","class complex\n","{\n","    double re;\n","    double im;\n","\n","public:\n","    // constructor\n","    complex(double r, double i): re {r}, im {i} { }\n","    complex(double r): re {r}, im {0} { }\n","    complex(): re {0}, im {0} { }\n","\n","    double real() const\n","    {\n","        return re;\n","    }\n","    void real(double d)\n","    {\n","        re=d;\n","    }\n","    double imag() const\n","    {\n","        return im;\n","    }\n","    void imag(double d)\n","    {\n","        im=d;\n","    }\n","\n","    complex& operator+=(complex z)\n","    {\n","        re+=z.re, im+=z.im;\n","        return *this;\n","    }\n","    complex& operator-=(complex z)\n","    {\n","        re-=z.re, im-=z.im;\n","        return *this;\n","    }\n","    complex& operator*=(complex);\n","    complex& operator/=(complex);\n","    \n","    bool complex::operator==(const complex &b) const;\n","\n","    friend std::ostream & operator<<(std::ostream& os, const complex& a);\n","\n","};\n","\n","complex& complex::operator*=(complex z)\n","{\n","    double tre = re;\n","    double tim = im;\n","    re = tre*z.re - tim*z.im;\n","    im = tre*z.im + tim*z.re;\n","    return *this;\n","}\n","complex& complex::operator/=(complex z)\n","{\n","    double tre = re;\n","    double tim = im;\n","    double norm = z.re*z.re + z.im*z.im;\n","    re = (tre*z.re + tim*z.im)/norm;\n","    im = (tim*z.re - tre*z.im)/norm;\n","    return *this;\n","}\n","\n","complex operator+(complex a, complex b) { return a+=b; }\n","complex operator-(complex a, complex b) { return a-=b; }\n","complex operator-(complex a)\n","{\n","    return complex{-a.real(), -a.imag()};\n","}\n","complex operator*(complex a, complex b) { return a*=b; }\n","complex operator/(complex a, complex b) { return a/=b; }\n","\n","bool operator==(complex a, complex b)\n","{\n","    return a.real()==b.real() && a.imag()==b.imag();\n","}\n","\n","bool operator!=(complex a, complex b) { return !(a==b); }\n","\n","ostream & operator<<(ostream& os, const complex& a)\n","{\n","    if (a.im>0)\n","        os << a.re << \"+\" << a.im <<  \"i\";\n","    else if (a.im<0)\n","        os << a.re <<  a.im << \"i\";   \n","    else\n","        os << a.re;\n","    return os;\n","}\n","\n","int main()\n","{\n","    complex a {2.3};\n","    complex b {2.5, -4.2};\n","    complex c {a+complex{1,2.3}};\n","    double z;\n","\n","    cout << a << endl;\n","    cout << b << endl;\n","    cout << c << endl;\n","\n","    if (z==a) {\n","\n","    }\n","\n","    if (a!=b) a+= b;\n","    cout << a << endl;\n","\n","}"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"GpNA2DsHhmxV","executionInfo":{"status":"ok","timestamp":1667914401432,"user_tz":-480,"elapsed":317,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"9584a29c-31ab-4ae5-d278-8ab6b1c4cd5e"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting E09_08.cpp\n"]}]},{"cell_type":"code","source":["%%shell\n","g++ E09_08.cpp -std=c++1z -o E09_08\n","./E09_08"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"dDOMMAiwh9LK","executionInfo":{"status":"ok","timestamp":1667914406525,"user_tz":-480,"elapsed":929,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"90b6e7e4-5ad0-4f3b-f188-16bc9a1c00f8"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["\u001b[01m\u001b[KE09_08.cpp:\u001b[m\u001b[K In function ‘\u001b[01m\u001b[Kbool operator==(complex, complex)\u001b[m\u001b[K’:\n","\u001b[01m\u001b[KE09_08.cpp:80:14:\u001b[m\u001b[K \u001b[01;31m\u001b[Kerror: \u001b[m\u001b[K‘\u001b[01m\u001b[Kdouble complex::re\u001b[m\u001b[K’ is private within this context\n","     return a.\u001b[01;31m\u001b[Kre\u001b[m\u001b[K==b.re && a.imag()==b.imag();\n","              \u001b[01;31m\u001b[K^~\u001b[m\u001b[K\n","\u001b[01m\u001b[KE09_08.cpp:8:12:\u001b[m\u001b[K \u001b[01;36m\u001b[Knote: \u001b[m\u001b[Kdeclared private here\n","     double \u001b[01;36m\u001b[Kre\u001b[m\u001b[K;\n","            \u001b[01;36m\u001b[K^~\u001b[m\u001b[K\n","\u001b[01m\u001b[KE09_08.cpp:80:20:\u001b[m\u001b[K \u001b[01;31m\u001b[Kerror: \u001b[m\u001b[K‘\u001b[01m\u001b[Kdouble complex::re\u001b[m\u001b[K’ is private within this context\n","     return a.re==b.\u001b[01;31m\u001b[Kre\u001b[m\u001b[K && a.imag()==b.imag();\n","                    \u001b[01;31m\u001b[K^~\u001b[m\u001b[K\n","\u001b[01m\u001b[KE09_08.cpp:8:12:\u001b[m\u001b[K \u001b[01;36m\u001b[Knote: \u001b[m\u001b[Kdeclared private here\n","     double \u001b[01;36m\u001b[Kre\u001b[m\u001b[K;\n","            \u001b[01;36m\u001b[K^~\u001b[m\u001b[K\n","2.3\n","2.5-4.2i\n","3.3+2.3i\n","4.8-4.2i\n"]},{"output_type":"execute_result","data":{"text/plain":[]},"metadata":{},"execution_count":73}]},{"cell_type":"markdown","source":["### 自訂有理數  Rational Class\n","\n","先決定要用甚麼表達方式\n","```\n","class Rational {\n","    int numer;\n","    int denom;\n","};\n","```\n","上面的兩個 `private` member 分別用來表示有理數的分子和分母。接下來要寫 constructor，讓 class 知道對於新產生的 object，應該如何設定初始狀態。\n","\n","底下是三種 constructors，有了這三個 constructors，可以產生型別為 Rational 的變數。\n","```\n","class Rational\n","{\n","    int numer;  // private members\n","    int denom;\n","public:\n","    Rational(): numer{0}, denom{1} {}\n","    Rational(int n): numer{n}, denom{1} {}\n","    Rational(int n, int d): numer{n}, denom{d} {}\n","};\n","```\n","\n","例如底下的寫法， `a` 的值會是 `0`，`b` 的值是 `3/1`，`c` 的值則是 `3/4`。\n","```\n","int main()\n","{\n","    Rational a;\n","    Rational b{3};\n","    Rational c{3,4};\n","}\n","```\n","\n","接下來定義 `show()`，讓我們可以把 `Rational` 的內容顯示出來。程式碼變成\n","```\n","#include <iostream>\n","using namespace std;\n","class Rational\n","{\n","    int numer;\n","    int denom;\n","public:\n","    Rational(): numer{0}, denom{1} {}\n","    Rational(int n): numer{n}, denom{1} {}\n","    Rational(int n, int d): numer{n}, denom{d} {}\n","    void show() {\n","        cout << numer << \"/\" << denom << endl;\n","    }\n","};\n","```\n","\n","然後在主程式裡面可以這樣用\n","```\n","int main()\n","{\n","    Rational a;\n","    Rational b{3};\n","    Rational c{3,4};\n","    a.show();\n","    b.show();\n","    c.show();\n","}\n","```"],"metadata":{"id":"CEKlQKSsiFHT"}},{"cell_type":"markdown","source":["除了 constructor，我們也要定義 assignment operator。\n","```\n","Rational& operator=(const Rational& b)\n","{\n","    numer = b.numer;\n","    denom = b.denom;\n","    return *this;\n","}\n","```\n","\n","參數定成 `const Rational& b` ，表示 `b` 是用 pass by reference 的方式傳參數，`b` 只是傳進來的參數的代名詞，並沒有複製一份，而是直接參考既有的內容，這時候如果在函數裡面改變 `b` 的內容，會直接改到原本傳進來的參數 (正版) 的內容，所以，除非真的打算這麼做，不然通常會多加上 `const`，表示在函數裡面不會動到原本的內容。\n","\n","Assignment operator 的用途是要把等號右邊的內容設定給等號左邊的變數，例如 \n","```\n","a = c;\n","``` \n","如果當成函數來看相當於 \n","```\n","a.=( c );\n","```\n","，`a` 是 `Rational` 物件，`=( )` 就是上面定義的 `a` 的 `operator=`  函數，`c` 是參數，所以在函數裡面，`b.numer` 和 `b.denom` 相當於取出 `c.numer` 和 `c.denom`。\n","\n","由於 `operator=` 是 `Rational` 的成員函數 (member function)，所以可以直接用 `.` 符號，存取 `private` 成員 `numer` 和 `denom`。\n","有了上面的 member functions，主程式 `main()` 裡面可以這樣使用\n","```\n","    a = c;\n","    a.show();\n","    a = Rational{3,5};\n","    a.show();\n","```\n","把一個 有理數的值設給另外一個有理數。 \n","\n","\n","替 `Rational` 加入下面的 `public` member functions (加在 `Rational  class`  的 `public:` 區段裡面)\n","```\n","int nu() const { return numer; }\n","int de() const { return denom; }\n","```\n","\n","在 `nu()` 和 `de()` 後面的 `const`，表示 `nu()` 和 `de()` 是 `const` member functions，不會更改 object 的內容。這兩個函數是為了提供介面，讓 `Rational` 成員函數以外的其他函數，也能夠取得 `private` 成員 `numer` 和 `denom` 的值。\n","\n","譬如  `operator+`，不是 `Rational` 的成員函數，當我們寫 \n","```\n","a + b\n","``` \n","的時候，相當於呼叫 \n","```\n","+(a,b)\n","```\n","雖然 `operator+` 不是 `Rational` 的成員函數，卻需要用到 `a` 和 `b` 兩個 `Rational` 的 `private` 內容，這時候就必須透過上面的 `nu()` 和 `de()` 來取得 `Rational` 的 `private` 成員 `numer` 和 `denom` 的值。\n","```\n","const Rational operator+(const Rational& lhs, const Rational& rhs)\n","{\n","    return Rational(lhs.nu()*rhs.de()+lhs.de()*rhs.nu(),\n","                    lhs.de()*rhs.de());\n","}\n","```\n","\n","接下來可以把其他 operator 一一定出來。其中 \n","```\n","+=    -=    *=    /=   ++    -- \n","```\n","都是 `Rational` 的成員函數，而 \n","```\n","+    -    *    /    <    ==\n","```\n","這些 binary  operator 都不是 `Rational` 的成員函數。其中 `<` 和 `==` 這兩個函數很有用，因為我們可以產生 `vector <Rational>`，也就是由 `Rational` 構成的向量，然後如果已經定義了 `<` 和 `==`，就可以利用現成的 `sort` 函數，對 `Rational` 向量排序。\n","\n","底下是稍微完整的 `Rational class` 的程式碼。\n","可以特別看一下 `<<` 符號的定義方式，這樣就能取代前面定義的 `show()`，改用 `cout <<` 的寫法顯示 `Rational` 的內容。另外也可以看一下 `++` 符號的定義方式，分成 `++a` 和 `a++` 兩種形式。\n","\n","\n","細節可參考 http://en.cppreference.com/w/cpp/language/operator_incdec\n"],"metadata":{"id":"fDGoXfI2ikPW"}},{"cell_type":"code","source":["%%writefile test.cpp\n","#include <iostream>\n","#include <vector>\n","#include <algorithm>\n","\n","using namespace std;\n","class Rational\n","{\n","    int numer;  // private members\n","    int denom;\n","public:\n","    Rational(): numer{0}, denom{1} { cout << \"call default constructor\\n\"; }\n","    Rational(int n): numer{n}, denom{1} { cout << \"call Rational(int n)\\n\";}\n","    Rational(int n, int d);\n","    Rational(const Rational& r): numer{r.numer}, denom{r.denom} \n","    {\n","        cout << \"call copy constructor\" << endl;\n","    }\n","    Rational& operator=(const Rational& b)\n","    {\n","        cout << \"call operator =\" << endl;\n","        numer = b.numer;\n","        denom = b.denom;\n","        return *this;\n","    }\n","    Rational& operator+=(const Rational& b)\n","    {\n","        numer = numer * b.denom + denom * b.numer;\n","        denom = denom * b.denom;\n","        return *this;\n","    }    \n","    void show() const {\n","        if (denom == 1) {\n","            cout << numer << endl;\n","        } else {\n","            cout << numer << \"/\" << denom << endl;\n","        }\n","    }\n","\n","    \n","    int nu() const { return numer; }\n","    int de() const { return denom; }\n","};\n","\n","Rational::Rational(int n, int d): numer{n}, denom{d}  { \n","    cout << \"Rational(int n, int d): \" << n << \"/\" << d << endl ;\n","}\n","\n","const Rational operator+(const Rational& lhs, const Rational& rhs)\n","{\n","    return Rational(lhs.nu()*rhs.de()+lhs.de()*rhs.nu(),\n","                    lhs.de()*rhs.de());\n","}\n","\n","\n","\n","bool operator<(const Rational& lhs, const Rational& rhs)\n","{\n","    return (lhs.nu()*rhs.de() - lhs.de()*rhs.nu()) < 0;\n","}\n","\n","\n","\n","bool operator==(const Rational& lhs, const Rational& rhs)\n","{\n","    return lhs.nu()*rhs.de() == lhs.de()*rhs.nu();\n","}\n","\n","ostream& operator<<(ostream& os, const Rational& r)\n","{\n","    os << r.nu() << \"/\" << r.de();\n","    return os;\n","}\n","\n","\n","\n","int main()\n","{\n","  Rational a;\n","  Rational b(3);\n","  Rational c(4, 5);\n","  Rational d(c);\n","\n","  vector<Rational> v{ Rational(3,4), Rational(2,3), Rational(4,5)};\n","\n","  for (const Rational& r : v) {\n","      cout << r << endl;\n","  }\n","  sort(v.begin(), v.end());\n","  for (const Rational& r : v) {\n","      cout << r << endl;\n","  }\n","\n","  \n","}"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"LxwdCMJt7NA7","executionInfo":{"status":"ok","timestamp":1668514737889,"user_tz":-480,"elapsed":313,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"3be55a4b-20a0-4bdf-c93b-624aef965ed5"},"execution_count":108,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting test.cpp\n"]}]},{"cell_type":"code","source":["%%shell\n","g++ test.cpp -o test -std=c++1z\n","./test"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"3_EdXKiS7-2w","executionInfo":{"status":"ok","timestamp":1668514660534,"user_tz":-480,"elapsed":1133,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"f27a72c1-dff4-4b61-ce4b-7a4958e2037d"},"execution_count":107,"outputs":[{"output_type":"stream","name":"stdout","text":["call default constructor\n","call Rational(int n)\n","Rational(int n, int d): 4/5\n","call copy constructor\n","Rational(int n, int d): 3/4\n","Rational(int n, int d): 2/3\n","Rational(int n, int d): 4/5\n","call copy constructor\n","call copy constructor\n","call copy constructor\n","3/4\n","2/3\n","4/5\n","call copy constructor\n","call operator =\n","call operator =\n","call copy constructor\n","call operator =\n","2/3\n","3/4\n","4/5\n"]},{"output_type":"execute_result","data":{"text/plain":[]},"metadata":{},"execution_count":107}]},{"cell_type":"code","source":["%%writefile E09_09.cpp\n","\n","#include <iostream>\n","#include <vector>\n","#include <algorithm>\n","\n","using namespace std;\n","\n","class Rational {\n","    int numer;\n","    int denom;\n","    int gcd(int a, int b)\n","    {\n","        if (b==0) return a;\n","        else return gcd(b, a%b);\n","    }\n","    int abs(int a)\n","    {\n","        return (a>0) ? a : -a;\n","    }\n","    void simplify()\n","    {\n","        bool negative = (numer*denom < 0);\n","        numer = abs(numer);\n","        denom = abs(denom);\n","        int g = gcd(numer, denom);\n","        numer = numer/g;\n","        denom = denom/g;\n","        if (negative) {\n","            numer = -numer;\n","        }\n","    }\n","public:\n","    Rational() { numer=0; denom=1; }\n","    Rational(int n): numer{n} { denom=1; }\n","    Rational(int n, int d): numer{n}, denom{d}\n","    {\n","        simplify();\n","    }\n","\n","    Rational(const Rational & r)\n","    {\n","        numer = r.numer;\n","        denom = r.denom;\n","    }\n","\n","    Rational& operator=(const Rational& r)\n","    {\n","        numer = r.numer;\n","        denom = r.denom;\n","        return *this;\n","    }\n","\n","    void show() const\n","    {\n","        if (denom==1)\n","            cout << numer << endl;\n","        else\n","            cout << numer << \"/\" << denom << endl;\n","    }\n","        int nu() const\n","    {\n","        return numer;\n","    }\n","    int de() const\n","    {\n","        return denom;\n","    }\n","\n","    Rational& operator+=(const Rational& r);\n","    Rational& operator-=(const Rational& r);\n","    Rational& operator*=(const Rational& r);\n","    Rational& operator/=(const Rational& r);\n","\n","    const Rational& operator++();\n","    const Rational& operator--();\n","    Rational operator++(int);\n","    Rational operator--(int);\n","};\n","\n","\n","Rational& Rational::operator+=(const Rational& r)\n","{\n","    numer = numer*r.denom+denom*r.numer;\n","    denom = denom*r.denom;\n","    simplify();\n","    return *this;\n","}\n","Rational& Rational::operator-=(const Rational& r)\n","{\n","    numer = numer*r.denom-denom*r.numer;\n","    denom = denom*r.denom;\n","    simplify();\n","    return *this;\n","}\n","Rational& Rational::operator*=(const Rational& r)\n","{\n","    numer = numer*r.numer;\n","    denom = denom*r.denom;\n","    simplify();\n","    return *this;\n","}\n","Rational& Rational::operator/=(const Rational& r)\n","{\n","    numer = numer*r.denom;\n","    denom = denom*r.numer;\n","    simplify();\n","    return *this;\n","}\n","\n","const Rational operator+(const Rational& lhs, const Rational& rhs)\n","{\n","    return Rational(lhs.nu()*rhs.de()+lhs.de()*rhs.nu(),\n","                    lhs.de()*rhs.de());\n","}\n","const Rational operator-(const Rational& lhs, const Rational& rhs)\n","{\n","    return Rational(lhs.nu()*rhs.de()-lhs.de()*rhs.nu(),\n","                    lhs.de()*rhs.de());\n","}\n","const Rational operator*(const Rational& lhs, const Rational& rhs)\n","{\n","    return Rational(lhs.nu()*rhs.nu(),\n","                    lhs.de()*rhs.de());\n","}\n","const Rational operator/(const Rational& lhs, const Rational& rhs)\n","{\n","    return Rational(lhs.nu()*rhs.de(),\n","                    lhs.de()*rhs.nu());\n","}\n","\n","bool operator==(const Rational& lhs, const Rational& rhs)\n","{\n","    return lhs.nu()==rhs.nu() &&\n","                    lhs.de()==rhs.de();\n","}\n","\n","bool operator<(const Rational& lhs, const Rational& rhs)\n","{\n","    Rational rat = lhs-rhs;\n","    return rat.nu()<0;\n","}\n","\n","ostream & operator<<(ostream& os, const Rational& a)\n","{\n","    if (a.de()==1)\n","        os << a.nu() ;\n","    else\n","        os << a.nu() << \"/\" << a.de();\n","    return os;\n","}\n","\n","// b = ++a;\n","const Rational& Rational::operator++()\n","{\n","    numer += denom;  \n","    return *this;\n","}\n","const Rational& Rational::operator--()\n","{\n","    numer -= denom;\n","    return *this;\n","}\n","\n","//  b = a++;\n","Rational Rational::operator++(int)\n","{\n","   Rational rat = *this;\n","   ++(*this);\n","   return rat;\n","}\n","Rational Rational::operator--(int)\n","{\n","   Rational rat = *this;\n","   --*this;\n","   return rat;\n","}\n","int main()\n","{\n","    Rational r{6, -5};\n","    Rational t{r};\n","    Rational z;\n","    r.show();\n","    t.show();\n","    z.show();\n","    z = t;\n","    z += t;\n","    z += Rational(2,3);\n","    z.show();\n","    z = t+2;\n","    z.show();\n","    z = 2/t;\n","    z.show();\n","    ++z;\n","    z.show();\n","    z--;\n","    z.show();\n","    cout << z << endl;\n","\n","    Rational a{2,3};\n","    Rational b{5,7};\n","\n","    // const Rational operator+(const Rational& a, const Rational& b);\n","    // the following statement is wrong because the left-hand-side is const\n","    // a+b = 3;\n","\n","    vector<Rational> vec{\n","        Rational(2,3),\n","        Rational(3,4),\n","        Rational(1,2)};\n","\n","    cout << \"before sorting\\n\";\n","    for (auto v: vec)\n","        cout << v << endl;\n","\n","    sort(vec.begin(), vec.end());\n","\n","    cout << \"after sorting\\n\";\n","    for (auto v: vec)\n","        cout << v << endl;\n","\n","}\n"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"csZ7ahXOjzhy","executionInfo":{"status":"ok","timestamp":1668515490162,"user_tz":-480,"elapsed":332,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"8fe3fbd5-0a00-40dd-95fa-952207011bcf"},"execution_count":111,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting E09_09.cpp\n"]}]},{"cell_type":"code","source":["%%shell\n","g++ E09_09.cpp -std=c++1z -o E09_09\n","./E09_09"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"Az8FHP4PktzP","executionInfo":{"status":"ok","timestamp":1668515496320,"user_tz":-480,"elapsed":814,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"9f582755-68e3-488b-add6-be188c91cbc5"},"execution_count":112,"outputs":[{"output_type":"stream","name":"stdout","text":["-6/5\n","-6/5\n","0\n","-26/15\n","4/5\n","-5/3\n","-2/3\n","-5/3\n","-5/3\n","before sorting\n","2/3\n","3/4\n","1/2\n","after sorting\n","1/2\n","2/3\n","3/4\n"]},{"output_type":"execute_result","data":{"text/plain":[]},"metadata":{},"execution_count":112}]}]}