[分享] C++和Python的相似處

看板C_and_CPP作者 (沒有存在感的人)時間9年前發表 (2016/05/03 18:46), 9年前編輯推噓31(31043)
留言74則, 27人參與, 最新討論串1/1
本文大部份內容來自此網頁: C++ Has Become More Pythonic http://preshing.com/20141202/cpp-has-become-more-pythonic/ --------------------------- 我以前也是覺得C++的語法看起來很複雜很討厭的人。 不過C++這幾年變了很多,C++11 和 C++14 加了很多新語法進去。 連C++的老爸 Bjarne Stroustrup http://www.stroustrup.com/C++11FAQ.html#think 都說: “It feels like a new language.” 該怎麼說呢?語法上愈來愈簡潔,愈來愈python化了... 不知道是python影響C++還是C++影響python? 以下就大致列出python跟C++的共同點。 ### 語法 (Literals) ### Python從2008年開始引進字面輸入二進位值(binary literals)。 gcc從2007年就支援(雖然C standard從來沒把這個加進去)。 C++是直到C++14 http://en.cppreference.com/w/cpp/language/integer_literal 才正式支援: static const int primes = 0b1010000010001010001010; 當字串中有**'\\'**的時候,以前你可能得這樣寫: const char* path = "(c:\\this\\string\\has\\backslashes)"; C++11 http://en.cppreference.com/w/cpp/language/string_literal 開始可以字面輸入原始字串(raw string literals)。 const char* path = R"(c:\this\string\has\backslashes)"; Python則是1998年就支援了。 ### 有範圍根據的 for 迴圈(Range-Based For Loops) ### Python裡要在同一個物件裡用迴圈把其中元素一一取出的話很簡單: for x in myList: print(x) 古早的C++寫起來很累贅 // array for (int i=0; i<10; ++i) std::cout<< A[i] << ' '; // vector(我討厭這種寫法...) for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) { /* std::cout << *it; ... */ } http://en.cppreference.com/w/cpp/language/range-for C++11開始你就能優雅地寫for loop: std::vector<int> v = {0, 1, 2, 3, 4, 5}; for(const int &i : v) // access by const reference std::cout << i << ' '; std::cout << '\n'; for(auto i: v) // access by value, the type of i is int std::cout << i << ' '; std::cout << '\n'; for(auto&& i: v) // access by reference, the type of i is int& std::cout << i << ' '; std::cout << '\n'; for(int n: {0, 1, 2, 3, 4, 5}) // the initializer may be a braced-init-list std::cout << n << ' '; std::cout << '\n'; int a[] = {0, 1, 2, 3, 4, 5}; for(int n: a) // the initializer may be an array std::cout << n << ' '; std::cout << '\n'; for(int n: a) std::cout << 1 << ' '; // the loop variable need not be used std::cout << '\n'; ### 自動指定型別 Auto ### Python有個方便的地方就是不用理會變數到底該用怎樣的型別(data type), 會自動幫你決定或改變。 C++以前可不是這樣,但是C++11 http://en.cppreference.com/w/cpp/language/auto 開始多了自動推斷類型的語法: auto x = "Hello world!"; std::cout << x; 就算是有函式重載(function overloading)的情況下, 呼叫函式時編譯器也會對於auto自動判別的型別指到相對應的函式去。 C++14更方便,連自動決定函式的回傳型別(Function return type deduction) https://en.wikipedia.org/wiki/C%2B%2B14#Function_return_type_deduction 都能辦到了。 auto Correct(int i) { if (i == 1) return i; // return type deduced as int else return Correct(i-1)+i; // ok to call it now } auto Wrong(int i) { if (i != 1) return Wrong(i-1)+i; // Too soon to call this. No prior return statement. else return i; // return type deduced as int } ### Tuples ### Python很久就有tuple這個能把不同型別的變數包在一起又不能修改的型別。 triple = (5, 6, 7) print(triple[0]) C++11開始也有了,甚至直接挑明這是跟Python致敬。 auto triple = std::make_tuple(5, 6, 7); std::cout << std::get<0>(triple); Python可以把tuple拆開 x, y, z = triple C++11當然也可以 std::tie(x, y, z) = triple; ### 統一初始化 (Uniform Initialization) ### Python可以當初始一個List類型時就把元素加進去,之後想增加也行。 Dictionary也可以這樣搞。 myList = [6, 3, 7, 8] myList.append(5); myDict = {5: "foo", 6: "bar"} print(myDict[5]) C++11 http://www.stroustrup.com/C++11FAQ.html#init-list 開始, vector、std::map 和 unordered_map也可以這樣搞了 auto myList = std::vector<int>{ 6, 3, 7, 8 }; myList.push_back(5); auto myDict = std::unordered_map<int, const char*>{ { 5, "foo" }, { 6, "bar" } }; std::cout << myDict[5]; ### Lambda 表達式 (Lambda Expressions) ### Python的Lambda是很神奇的東西... myList.sort(key = lambda x: abs(x)) C++11 http://www.stroustrup.com/C++11FAQ.html#lambda 居然也有了... (可我還是比較喜歡函式指標阿....) std::sort(myList.begin(), myList.end(), [](int x, int y){ return std::abs(x) < std::abs(y); }); ### Standard Algorithms ### Python可以用內建的filter函式把符合條件的元素複製出來... result = filter(lambda x: x >= 0, myList) C++11 http://en.cppreference.com/w/cpp/algorithm/copy 的 std::copy_if有著相似的功能... (怎麼突然有種好神奇的感覺?) auto result = std::vector<int>{}; std::copy_if(myList.begin(), myList.end(), std::back_inserter(result), [](int x){ return x >= 0; }); ### Parameter Packs ### Python這個功能我還真沒用過,實際用法可以參考這裡 http://hangar.runway7.net/python/packing-unpacking-arguments def func1(x, y, z): print x print y print z def func2(*args): # Convert args tuple to a list so we can modify it args = list(args) args[0] = 'Hello' args[1] = 'awesome' func1(*args) func2('Goodbye', 'cruel', 'world!') # Will print # > Hello # > awesome # > world! C++11 也能這樣做 http://www.stroustrup.com/C++11FAQ.html#variadic-templates template <typename... T> auto foo(T&&... args) { return std::make_tuple(args...); } ... auto triple = foo(5, 6, 7); 看到這裡,是不是覺得C++變得愈來愈優雅了? -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 90.41.1.211 ※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1462301210.A.9FC.html ※ 編輯: wtchen (90.41.1.211), 05/04/2016 02:56:44

05/04 09:26, , 1F
05/04 09:26, 1F

05/04 10:06, , 2F
說到 param pack,昨天好奇去看了C++17 的 fold expr
05/04 10:06, 2F

05/04 10:06, , 3F
覺得這語法超神奇的XDDD
05/04 10:06, 3F

05/04 10:07, , 4F
C++在這幾年真的是不斷進化,都快不認識了
05/04 10:07, 4F

05/04 10:28, , 5F
建議板大連結不要放括號裡,會帶到瀏覽器,造成錯誤
05/04 10:28, 5F
拿掉了,因為本來是用kramdown寫的。

05/04 10:44, , 6F
這篇不錯耶
05/04 10:44, 6F

05/04 10:51, , 7F
推 for 對 vector的用法太實用了
05/04 10:51, 7F

05/04 11:28, , 8F
老實說你講的這幾點都不是python獨有/獨創
05/04 11:28, 8F

05/04 14:11, , 9F
沒人說這些是 Python 獨有/創新, 但 Python 是極少數擁
05/04 14:11, 9F

05/04 14:11, , 10F
有「全部」的語言 -- 現在 C++ 也加入這個榮譽俱樂部了
05/04 14:11, 10F

05/04 14:13, , 11F
希望 C++ 未來也能有 enhanced proposal 機制
05/04 14:13, 11F

05/04 14:13, , 12F
(打錯, 是 enhancement proposal)
05/04 14:13, 12F

05/04 14:33, , 13F
lambda 是從 fp 來的,最早在 lisp 就已經有了.
05/04 14:33, 13F

05/04 14:33, , 14F
Java 是從 8 開始才有,但基本上現在的 Python,
05/04 14:33, 14F

05/04 14:34, , 15F
對 lambda 依賴已經沒有以前高,用型別運算產生式就行.
05/04 14:34, 15F

05/04 14:38, , 16F
另外,以前蔡學鏞推過一個 rebol.不過這幾年沒啥進步..
05/04 14:38, 16F

05/04 14:39, , 17F
如果對 C++ 難分難捨,那也許也可以留意一下 Rust
05/04 14:39, 17F
※ 編輯: wtchen (90.41.1.211), 05/04/2016 15:27:43

05/04 15:37, , 18F
有大大可以寫篇Rust跟C++的比較文嗎?保證不砍
05/04 15:37, 18F

05/04 15:58, , 19F
說得也是
05/04 15:58, 19F

05/04 15:59, , 20F
推 Rust
05/04 15:59, 20F

05/04 16:25, , 21F
Parameter Packs 其實就是未指定參數名稱,就都算它的.
05/04 16:25, 21F

05/04 16:26, , 22F
比如 x= ,y= , a,b,c ,沒有 = 指定輸入值,就全給 *arg
05/04 16:26, 22F

05/04 16:31, , 23F
所以 func1 把 arg 前兩個換掉再丟進 func2 去配對.
05/04 16:31, 23F

05/04 16:32, , 24F
2 1
05/04 16:32, 24F

05/04 16:34, , 25F
一般看到的範例是 func1(x, *args) 這種型式.
05/04 16:34, 25F

05/04 16:34, , 26F
沒被指定參數名稱抓到的,就全送進 *arg
05/04 16:34, 26F

05/04 16:36, , 27F
C++11之後,C跟C++的分歧就越來越大了....
05/04 16:36, 27F

05/04 16:38, , 28F
其實可以更徹底一點XD 為了兼容C實在犧牲太大了..
05/04 16:38, 28F

05/04 16:43, , 29F
python 3.x 還有 **args 抓進去直接配成 dict 的 k:v
05/04 16:43, 29F

05/04 17:16, , 30F
與C相容…前朝遺毒 (誤
05/04 17:16, 30F

05/04 21:38, , 31F
應該說...歷史包袱(?
05/04 21:38, 31F

05/04 21:56, , 32F
推 有些寫法第一次看到 學習了
05/04 21:56, 32F

05/04 22:43, , 33F
05/04 22:43, 33F

05/04 23:45, , 34F
C++11之後就是突破大氣層的感覺
05/04 23:45, 34F

05/05 00:11, , 35F
final跟override也不錯
05/05 00:11, 35F

05/05 00:17, , 36F
variadic template搭配多重繼承也蠻有趣的
05/05 00:17, 36F

05/05 04:38, , 37F
for(auto&& i: v) // access by reference, the type of
05/05 04:38, 37F

05/05 04:38, , 38F
對這個有點問題, 如果auto&& 是by reference 那auto&呢?
05/05 04:38, 38F

05/05 07:18, , 39F
推 寫得很棒
05/05 07:18, 39F

05/05 07:53, , 40F
auto&&產生ref,auto&產生左值ref
05/05 07:53, 40F

05/05 10:51, , 41F
偷推yoco大神的文章 #19gioP8j
05/05 10:51, 41F

05/05 12:01, , 42F
大推~
05/05 12:01, 42F

05/05 12:07, , 43F
auto&&產生型態為右值ref的左值ref
05/05 12:07, 43F

05/05 12:08, , 44F
auto&參考到一個左值 不產生新物件
05/05 12:08, 44F

05/05 12:22, , 45F
auto &是l value ref,auto &&是r value ref
05/05 12:22, 45F

05/05 12:33, , 46F
不對,auto&&不是專門用在rvalue ref
05/05 12:33, 46F

05/05 12:34, , 47F
其中有用到跟template一樣的折疊規則
05/05 12:34, 47F

05/05 12:45, , 48F
對不起,我錯了。有具名的&&才是r value ref。auto &&是
05/05 12:45, 48F

05/05 12:45, , 49F
forwarding reference
05/05 12:45, 49F

05/05 12:47, , 50F
簡單來說,auto &&i:v要看v是l value還是r value。如果
05/05 12:47, 50F

05/05 12:47, , 51F
v是l value,那auto &&i就是int &i;如果
05/05 12:47, 51F

05/05 12:48, , 52F
v是r value,那auto &&i就是int &&i
05/05 12:48, 52F

05/05 16:02, , 53F
請問什麼是有具名的&&?
05/05 16:02, 53F

05/05 16:09, , 54F
應該說,有具型態的才對。例如直接指定int &&,此時已經
05/05 16:09, 54F

05/05 16:10, , 55F
指定型態是int,而不是用auto幫你進行type deduction
05/05 16:10, 55F

05/05 16:36, , 56F
注意const auto&&不適用折疊規則,此時就是rvalue ref
05/05 16:36, 56F

05/05 16:36, , 57F
跟template的推導規則一樣
05/05 16:36, 57F

05/05 17:46, , 58F
簡單的說,reference to rvalue 本身也是一個 lvalue
05/05 17:46, 58F

05/05 19:01, , 59F
auto 跟重載那段有點怪, 這兩個沒關聯吧
05/05 19:01, 59F

05/05 19:03, , 60F
Python lambda 的語法限制有點詭異. Python 不太推lambda
05/05 19:03, 60F

05/05 19:04, , 61F
我自己是不太喜歡用lambda,太簡化的東西反而反應不過來
05/05 19:04, 61F

05/05 19:05, , 62F
parameter pack 跟 variadic template 很不同吧?
05/05 19:05, 62F

05/05 19:19, , 63F
A template with at least one parameter pack is
05/05 19:19, 63F

05/05 19:19, , 64F
called a variadic template.
05/05 19:19, 64F

05/05 19:45, , 65F
STL如果沒lambda,functor寫起來會很痛苦
05/05 19:45, 65F

05/06 11:17, , 66F
Python也只是跟別人學來的,C++剛好學到一樣的部分而已z
05/06 11:17, 66F

05/06 11:23, , 67F
這些特性早在Ada,ML,Scheme就有了,他們都比Python老=.=
05/06 11:23, 67F

05/06 16:30, , 68F
現代程式語言不都互相影響嗎,真要考古誰原創考不完w
05/06 16:30, 68F

05/06 19:34, , 69F
只求好寫、好偵錯、速度快又不用造輪子,誰抄誰不重要
05/06 19:34, 69F

05/07 22:39, , 70F
推整理
05/07 22:39, 70F

05/07 22:41, , 71F
好文!匿名函式這招實在太好用
05/07 22:41, 71F

05/08 01:46, , 72F
推... 希望C++17把平行化也好好的弄出來:>
05/08 01:46, 72F

05/16 02:53, , 73F
跟 Python 背景的朋友聊天講到 C++ Templates 的時候, 常以
05/16 02:53, 73F

05/16 02:53, , 74F
compile-time dock-type 類比
05/16 02:53, 74F
文章代碼(AID): #1NAF8Qdy (C_and_CPP)