• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆-341  評(píng)論-2670  文章-0  trackbacks-0

            這個(gè)系列的起因是這樣的,王垠寫(xiě)了一篇噴go的博客http://www.yinwang.org/blog-cn/2013/04/24/go-language/,里面說(shuō)go已經(jīng)爛到無(wú)可救藥了,已經(jīng)懶得說(shuō)了,所以讓大家去看http://www.mindomo.com/view.htm?m=8cc4f95228f942f8886106d876d1b041,里面有詳細(xì)的解釋。然后這篇東西被發(fā)上了微博,很多博友立刻展示了人性丑陋的一面:
            1、那些go的擁護(hù)者們,因?yàn)間o被噴了,就覺(jué)得自己的人格受到了侮辱一樣,根本來(lái)不及看到最后一段的鏈接,就開(kāi)始張牙舞爪。
            2、王垠這個(gè)人的確是跟人合不來(lái),所以很多人就這樣斷定他的東西“毫無(wú)參考價(jià)值”。

            不過(guò)說(shuō)實(shí)話(huà),文章里面是噴得有點(diǎn)不禮貌,這也在一定程度上阻止了那些不學(xué)無(wú)術(shù)的人們繼續(xù)閱讀后面的精華部分。如果所有的文章都這樣那該多好啊,那么爛人永遠(yuǎn)都是爛人,不糾正自己的心態(tài)永遠(yuǎn)獲得不了任何有用的知識(shí),永遠(yuǎn)過(guò)那種月入一蛆的日子,用垃圾的語(yǔ)言痛苦的寫(xiě)一輩子沒(méi)價(jià)值的程序。

            廢話(huà)就說(shuō)到這里了,下面我來(lái)說(shuō)說(shuō)我自己對(duì)于語(yǔ)言的觀(guān)點(diǎn)。為什么要設(shè)計(jì)一門(mén)新語(yǔ)言?原因無(wú)非就兩個(gè),要么舊的語(yǔ)言實(shí)在是讓人受不了,要么是針對(duì)領(lǐng)域設(shè)計(jì)的專(zhuān)用語(yǔ)言。后一種我就不講了,因?yàn)槿绻麤](méi)有具體的領(lǐng)域知識(shí)的話(huà),這種東西永遠(yuǎn)都做不好(譬如SQL永遠(yuǎn)不可能出自一個(gè)數(shù)據(jù)庫(kù)很爛的人手里),基本上這不是什么語(yǔ)言設(shè)計(jì)的問(wèn)題。所以這個(gè)系列只會(huì)針對(duì)前一種情況——也就是設(shè)計(jì)一門(mén)通用的語(yǔ)言。通用的語(yǔ)言其實(shí)也有自己的“領(lǐng)域”,只是太多了,所以被淡化了。縱觀(guān)歷史,你讓一個(gè)只做過(guò)少量的領(lǐng)域的人去設(shè)計(jì)一門(mén)語(yǔ)言,如果他沒(méi)有受過(guò)程序設(shè)計(jì)語(yǔ)言理論的系統(tǒng)教育,那只能做出屎。譬如說(shuō)go就是其中一個(gè)——雖然他爹很牛逼,但反正不包含“設(shè)計(jì)語(yǔ)言”這個(gè)事情。

            因此,在21世紀(jì)你還要做一門(mén)語(yǔ)言,無(wú)非就是對(duì)所有的通用語(yǔ)言都不滿(mǎn)意,所以你想自己做一個(gè)。不滿(mǎn)意體現(xiàn)在什么方面?譬如說(shuō)C#的原因可能就是他爹不夠帥啦,譬如說(shuō)C++的原因可能就是自己智商太低hold不住啦,譬如說(shuō)Haskell的原因可能就是用的人太少招不到人啦,譬如說(shuō)C的原因可能就是實(shí)在是無(wú)法完成人和抽象所以沒(méi)有l(wèi)inus的水平的人都會(huì)把C語(yǔ)言寫(xiě)成屎但是你又招不到linus啦,總之有各種各樣的原因。不過(guò)排除使用者的智商因素來(lái)講,其實(shí)有幾個(gè)語(yǔ)言我還是很欣賞的——C++、C#、Haskell、Rust和Ruby。如果要我給全世界的語(yǔ)言排名,前五名反正是這五個(gè),雖然他們之間可能很難決出勝負(fù)。不過(guò)就算如此,其實(shí)這些語(yǔ)言也有一些讓我不爽的地方,讓我一直很想做一個(gè)新的語(yǔ)言(來(lái)給自己用(?)),證據(jù)就是——“看我的博客”。

            那么。一個(gè)好的語(yǔ)言的好,體現(xiàn)在什么方面呢?一直以來(lái),人們都覺(jué)得,只有庫(kù)好用,語(yǔ)言才會(huì)好用。其實(shí)這完全是顛倒了因果關(guān)系,如果沒(méi)有好用的語(yǔ)法,怎么能寫(xiě)出好用的庫(kù)呢?要找例子也很簡(jiǎn)單,只要比較一下Java和C#就夠了。C#的庫(kù)之所以好用,跟他語(yǔ)言的表達(dá)能力強(qiáng)是分不開(kāi)的,譬如說(shuō)linq(,to xml,to sql,to parser,etc),譬如說(shuō)WCF(僅考慮易用性部分),譬如說(shuō)WPF。Java能寫(xiě)得出來(lái)這些庫(kù)嗎?硬要寫(xiě)還是可以寫(xiě)的,但是你會(huì)發(fā)現(xiàn)你無(wú)論如何都沒(méi)辦法把他們做到用起來(lái)很順手的樣子,其實(shí)這都是因?yàn)镴ava的語(yǔ)法垃圾造成的。這個(gè)時(shí)候可以抬頭看一看我上面列出來(lái)的五種語(yǔ)言,他們的特點(diǎn)都是——因?yàn)檎Z(yǔ)法的原因,庫(kù)用起來(lái)特別爽。

            當(dāng)然,這并不要求所有的人都應(yīng)該把語(yǔ)言學(xué)習(xí)到可以去寫(xiě)庫(kù)。程序員的分布也是跟金字塔的結(jié)構(gòu)一樣的,庫(kù)讓少數(shù)人去寫(xiě)就好了,大多數(shù)人盡管用,也不用學(xué)那么多,除非你們想成為寫(xiě)庫(kù)的那些。不過(guò)最近有一個(gè)很不好的風(fēng)氣,就是有些人覺(jué)得一個(gè)語(yǔ)言難到自己無(wú)法【輕松】成為寫(xiě)庫(kù)的人,就開(kāi)始說(shuō)他這里不好那里不好了,具體都是誰(shuí)我就不點(diǎn)名了,大家都知道,呵呵呵。

            好的語(yǔ)言,除了庫(kù)寫(xiě)起來(lái)又容易又好用以外,還有兩個(gè)重要的特點(diǎn):容易學(xué),容易分析。關(guān)于容易學(xué)這一點(diǎn),其實(shí)不是說(shuō),你隨便看一看就能學(xué)會(huì),而是說(shuō),只要你掌握了門(mén)道,很多未知的特性你都可以猜中。這就有一個(gè)語(yǔ)法的一致性問(wèn)題在里面了。語(yǔ)法的一致性問(wèn)題,是一個(gè)很容易讓人忽略的問(wèn)題,因?yàn)樗幸驗(yàn)檎Z(yǔ)法的一致性不好而引發(fā)的錯(cuò)誤,原因都特別的隱晦,很難一眼看出來(lái)。這里我為了讓大家可以建立起這個(gè)概念,我來(lái)舉幾個(gè)例子。

            第一個(gè)例子是我們喜聞樂(lè)見(jiàn)的C語(yǔ)言的指針變量定義啦:

            int a, *b, **c;

            相信很多人都被這種東西坑過(guò),所以很多教科書(shū)都告訴我們,當(dāng)定義一個(gè)變量的時(shí)候,類(lèi)型最后的那些星號(hào)都要寫(xiě)在變量前面,避免讓人誤解。所以很多人都會(huì)想,為什么要設(shè)計(jì)成這樣呢,這明顯就是挖個(gè)坑讓人往下跳嘛。但是在實(shí)際上,這是一個(gè)語(yǔ)法的一致性好的例子,至于為什么他是個(gè)坑,問(wèn)題在別的地方。

            我們都知道,當(dāng)一個(gè)變量b是一個(gè)指向int的指針的時(shí)候,*b的結(jié)果就是一個(gè)int。定義一個(gè)變量int a;也等于在說(shuō)“定義a是一個(gè)int”。那我們來(lái)看上面那個(gè)變量聲明:int *b;。這究竟是在說(shuō)什么呢?其實(shí)真正的意思是“定義*b是一個(gè)int”。這種“定義和使用相一致”的方法其實(shí)正是我們要推崇的。C語(yǔ)言的函數(shù)定義參數(shù)用逗號(hào)分隔,調(diào)用的時(shí)候也用逗號(hào)分隔,這是好的。Pascal語(yǔ)言的函數(shù)定義參數(shù)用分號(hào)分隔,調(diào)用的時(shí)候用逗號(hào)分隔,這個(gè)一致性就少了一點(diǎn)。

            看到這里你可能會(huì)說(shuō),你怎么知道C語(yǔ)言他爹就是這么想的呢?我自己覺(jué)得如果他不是這么想的估計(jì)也不會(huì)差到哪里去,因?yàn)檫€有下面一個(gè)例子:

            int F(int a, int b);
            int (*f)(int a, int b);

            這也是一個(gè)“定義和使用相一致”的例子。就第一行代碼來(lái)說(shuō),我們要如何看待“int F(int a, int b);”這個(gè)寫(xiě)法呢?其實(shí)跟上面一樣,他說(shuō)的是“定義F(a, b)的結(jié)果為int”。至于a和b是什么,他也告訴你:定義a為int,b也為int。所以等價(jià)的,下面這一行也是“定義(*f)(a, b)的結(jié)果為int”。函數(shù)類(lèi)型其實(shí)也是可以不寫(xiě)參數(shù)名的,不過(guò)我們還是鼓勵(lì)把參數(shù)名寫(xiě)進(jìn)去,這樣Visual Studio的intellisense會(huì)讓你在敲“(”的時(shí)候把參數(shù)名給你列出來(lái),你看到了提示,有時(shí)候就不需要回去翻源代碼了。

            關(guān)于C語(yǔ)言的“定義和使用相一致”還有最后一個(gè)例子,這個(gè)例子也是很美妙的:

            int a;
            typedef int a;
            
            int (*f)(int a, int b);
            typedef int (*f)(int a, int b);

            typedef是這樣的一個(gè)關(guān)鍵字:他把一個(gè)符號(hào)從變量給修改成了類(lèi)型。所以每當(dāng)你需要給一個(gè)類(lèi)型名一個(gè)名字的時(shí)候,就先想一想,怎么定義一個(gè)這個(gè)類(lèi)型的變量,寫(xiě)出來(lái)之后往前面加個(gè)typedef,事情就完成了。

            不過(guò)說(shuō)實(shí)話(huà),就一致性來(lái)講,C語(yǔ)言也就到此為止了。至于說(shuō)為什么,因?yàn)樯厦孢@幾條看起來(lái)很美好的“定義和使用相一致”的規(guī)則是不能組合的,譬如說(shuō)看下面這一行代碼:

            typedef int(__stdcall*f[10])(int(*a)(int, int));
            這究竟是個(gè)什么東西呢,誰(shuí)看得清楚呀!而且這也沒(méi)辦法用上面的方法來(lái)解釋了。究其原因,就是C語(yǔ)言采用的這種“定義和使用相一致”的手法剛好是一種解方程的手法。譬如說(shuō)int *b;定義了“*b是int”,那b是什么呢,我們看到了之后,都得想一想。人類(lèi)的直覺(jué)是有話(huà)直說(shuō)開(kāi)門(mén)見(jiàn)山,所以如果我們知道int*是int的指針,那么int* b也就很清楚了——“b是int的指針”。 

            因?yàn)镃語(yǔ)言的這種做法違反了人類(lèi)的直覺(jué),所以這條本來(lái)很好的原則,采用了錯(cuò)誤的方法來(lái)實(shí)現(xiàn),結(jié)果就導(dǎo)致了“坑”的出現(xiàn)。因?yàn)榇蠹叶剂?xí)慣“int* a;”,然后C語(yǔ)言告訴大家其實(shí)正確的做法是“int *a;”,那么當(dāng)你接連的出現(xiàn)兩三個(gè)變量的時(shí)候,問(wèn)題就來(lái)了,你就掉坑里去了。

            這個(gè)時(shí)候我們?cè)倩仡^看一看上面那一段長(zhǎng)長(zhǎng)的函數(shù)指針數(shù)組變量的聲明,會(huì)發(fā)現(xiàn)其實(shí)在這種時(shí)候,C語(yǔ)言還是希望你把它看成“int* b;”的這種形式的:f是一個(gè)數(shù)組,數(shù)組返回了一個(gè)函數(shù)指針,函數(shù)返回int,函數(shù)的參數(shù)是int(*a)(int, int)所以他還是一個(gè)函數(shù)指針。

            我們?yōu)槭裁磿?huì)覺(jué)得C語(yǔ)言在這一個(gè)知識(shí)點(diǎn)上特別的難學(xué),就是因?yàn)樗瑫r(shí)混用了兩種原則來(lái)設(shè)計(jì)語(yǔ)法。那你說(shuō)好的設(shè)計(jì)是什么呢?讓我們來(lái)看看一些其它的語(yǔ)言的作法:

            C++:
            function<int __stdcall(function<int(int, int)>)> f[10];
            
            C#:
            Func<Func<int, int, int>, int>[] f;
            
            Haskell:
            f :: [(int->int->int)->int]
            
            Pascal:
            var f : array[0..9] of function(a : function(x : integer; y : integer):integer):integer;

             

            這些語(yǔ)言的做法,雖然并沒(méi)有遵守“定義和使用相一致”的原則,但是他們比C語(yǔ)言好的地方在于,他們只采用一種原則——這就比好的和壞的混在一起要強(qiáng)多了(這一點(diǎn)go也是,做得比C語(yǔ)言更糟糕)。

            當(dāng)然,上面這個(gè)說(shuō)法對(duì)Haskell來(lái)說(shuō)其實(shí)并不公平。Haskell是一種帶有完全類(lèi)型推導(dǎo)的語(yǔ)言,他不認(rèn)為類(lèi)型聲明是聲明的一部分,他把類(lèi)型聲明當(dāng)成是“提示”的一部分。所以實(shí)際上當(dāng)你真的需要一個(gè)這種復(fù)雜結(jié)構(gòu)的函數(shù)的時(shí)候,實(shí)際上你并不會(huì)真的去把它的類(lèi)型寫(xiě)出來(lái),而是通過(guò)寫(xiě)一個(gè)正確的函數(shù)體,然后讓Haskell編譯器幫你推導(dǎo)出正確的類(lèi)型。我來(lái)舉個(gè)例子:

            superApply fs x = (foldr id (.) fs) x

             

            關(guān)于foldr有一個(gè)很好的理解方法,譬如說(shuō)foldr 0 (+) [1,2,3,4]說(shuō)的就是1 + (2 + (3 + (4 + 0)))。而(.)其實(shí)是一個(gè)把兩個(gè)函數(shù)合并成一個(gè)的函數(shù):f (.) g = \x->f(g( x ))。所以上述代碼的意思就是,如果我有下面的三個(gè)函數(shù):

            add1 x = x + 1
            mul2 x = x * 2
            sqr x = x * x

             

            那么當(dāng)我寫(xiě)下下面的代碼的時(shí)候:

            superApply [sqr, mul2, add1] 1
            的時(shí)候,他做的其實(shí)是sqr(mul2(add1(1)) = ((1+1)*2) * ((1+1)*2) = 16。當(dāng)然,Haskell還可以寫(xiě)得更直白:
            superApply [(\x->x*x), (*2), (+1)] 1

             

            Haskell代碼的簡(jiǎn)潔程度真是喪心病狂啊,因?yàn)槿绻覀円肅++來(lái)寫(xiě)出對(duì)應(yīng)的東西的話(huà)(C語(yǔ)言的參數(shù)無(wú)法是一個(gè)帶長(zhǎng)度的數(shù)組類(lèi)型所以其實(shí)是寫(xiě)不出等價(jià)的東西的),會(huì)變成下面這個(gè)樣子:

            template<typename T>
            T SuperApply(const vector<function<T(T)>>& fs, const T& x)
            {
                T result = x;
                for(int i=fs.size()-1; i>=0; i--)
                {
                    result = fs[i](result);
                }
                return result;
            }

             

            C++不僅要把每一個(gè)步驟寫(xiě)得很清楚,而且還要把類(lèi)型描述出來(lái),整個(gè)代碼就變得特別的混亂。除此之外,C++還沒(méi)辦法跟Haskell一樣吧三個(gè)函數(shù)直接搞成一個(gè)vector然后送進(jìn)這個(gè)SuperApply里面直接調(diào)用。當(dāng)然有人會(huì)說(shuō),這還不是因?yàn)镠askell里面有foldr嘛。那讓我們來(lái)看看同樣有foldr(reverse + aggregate = foldr)的C#會(huì)怎么寫(xiě):

            T SuperApply<T>(Func<T, T>[] fs, T x)
            {
                return (fs
                    .Reverse()
                    .Aggregate(x=>x, (a, b)=>y=>b(a(y)))
                    )(x);
            }

             

            C#基本上已經(jīng)達(dá)到跟Haskell一樣的描述過(guò)程了,而且也可以寫(xiě)出下面的代碼了,就是無(wú)論聲明和使用的語(yǔ)法的噪音稍微有點(diǎn)大……

            SuperApply(new Func<T, T>[]{
                x=>x*x,
                x=>x*2,
                x=>x+1
                }, 1);

             

            為什么要在討論語(yǔ)法的一致性的時(shí)候說(shuō)這些問(wèn)題呢,在這里我想向大家展示Haskell的另一種“定義和使用相一致”的做法。Haskell整個(gè)語(yǔ)言都要用pattern matching去理解,所以上面的這段代碼

            superApply fs x = (foldr id (.) fs) x
            說(shuō)的是,凡是你出現(xiàn)類(lèi)似superApply a b的這種“pattern”,你都可以把它當(dāng)成(foldr id (.) a) b來(lái)看。譬如說(shuō)
            superApply [(\x->x*x), (*2), (+1)] 1
            其實(shí)就是
            (foldr id (.) [(\x->x*x), (*2), (+1)]) 1
            只要superApply指的是這個(gè)函數(shù),那無(wú)論在什么上下文里面,你都可以放心的做這種替換而程序的意思絕對(duì)不會(huì)有變化——這就是haskell的帶有一致性的原則。那讓我們來(lái)看看Haskell是如何執(zhí)行他這個(gè)一致性的。在這里我們需要知道一個(gè)東西,就是如果我們有一個(gè)操作符+,那我們要把+當(dāng)成函數(shù)來(lái)看,我們就要寫(xiě)(+)。如果我們有一個(gè)函數(shù)f,如果我們要把它當(dāng)成操作符來(lái)看,那就要寫(xiě)成`f`(這是按鍵!左邊的那個(gè)符號(hào))。因此Haskell其實(shí)允許我們做下面的聲明:
            (Point x y) + (Point z w) = Point (x+z) (y+w)
            (+) (Point x y) (Point z w) = Point (x+z) (y+w)
            
            (Point x y) `Add` (Point z w) = Point (x+z) (y+w)
            Add (Point x y) (Point z w) = Point (x+z) (y+w)

             

            斐波那契數(shù)列的簡(jiǎn)單形式甚至還可以這么寫(xiě):

            f 1 = 1
            f 2 = 1
            f (n+2) = f(n+1) + f(n)

             

            甚至連遞歸都可以寫(xiě)成:

            GetListLength [] = 0
            GetListLength (x:xs) = 1 + GetListLength xs

             

            Haskell到處都貫徹了“函數(shù)和操作符的替換關(guān)系”和“pattern matching”兩個(gè)原則來(lái)做“定義和實(shí)現(xiàn)相一致”的基礎(chǔ),從而實(shí)現(xiàn)了一個(gè)比C語(yǔ)言那個(gè)做了一半的混亂的原則要好得多的原則。

            有些人可能會(huì)說(shuō),Haskell寫(xiě)遞歸這么容易,那會(huì)不會(huì)因?yàn)楣膭?lì)人們寫(xiě)遞歸,而整個(gè)程序充滿(mǎn)了遞歸,很容易stack overflow或者降低運(yùn)行效率呢?在這里你可以往上翻,在這篇文章的前面有一句話(huà)“好的語(yǔ)言,除了庫(kù)寫(xiě)起來(lái)又容易又好用以外,還有兩個(gè)重要的特點(diǎn):容易學(xué),容易分析。”,這在Haskell里面體現(xiàn)得淋漓盡致。

            我們知道循環(huán)就是尾遞歸,所以如果我們把代碼寫(xiě)成尾遞歸,那Haskell的編譯器就會(huì)識(shí)別出來(lái),從而在生成x86代碼的時(shí)候把它處理成循環(huán)。一個(gè)尾遞歸遞歸函數(shù)的退出點(diǎn),要么是一個(gè)不包含自身函數(shù)調(diào)用的表達(dá)式,要么就是用自身函數(shù)來(lái)和其它參數(shù)來(lái)調(diào)用。聽(tīng)起來(lái)比較拗口,不過(guò)說(shuō)白了其實(shí)就是:

            GetListLength_ [] c = c
            GetListLength_ (x:xs) c = GetListLength_ xs (c+1)
            GetListLength xs = GetListLength_ xs 0

             

            當(dāng)你寫(xiě)出這樣的代碼的時(shí)候,Haskell把你的代碼編譯了之后,就會(huì)真的輸出一個(gè)循環(huán),從而上面的擔(dān)心都一掃而空。

            實(shí)際上,有很多性能測(cè)試都表明,在大多數(shù)平臺(tái)上,Haskell的速度也不會(huì)被C/C++慢超過(guò)一倍的同時(shí),要遠(yuǎn)比go的性能高出許多。在Windows上,函數(shù)式語(yǔ)言最快的是F#。Linux上則是Scala。Haskell一直都是第二名,但是只比第一名慢一點(diǎn)點(diǎn)。

            為了不讓文章太長(zhǎng),好分成若干次發(fā)布,每次間隔都較短,所以今天的坑我只想多講一個(gè)——C++的指針的坑。剩下的坑留到下一篇文章里面。下面要講的這個(gè)坑,如果不是在粉絲群里面被問(wèn)了,我還不知道有人會(huì)這么做:

            class Base
            {
              ...
            };
            
            class Derived : public Base
            {
              ...
            };
            
            Base* bs = new Derived[10];
            delete[] bs;

             

            我想說(shuō),這完全是C++兼容C語(yǔ)言,然后讓C語(yǔ)言給坑了。其實(shí)這個(gè)問(wèn)題在C語(yǔ)言里面是不會(huì)出現(xiàn)的,因?yàn)镃語(yǔ)言的指針其實(shí)說(shuō)白了只有一種:char*。很多C語(yǔ)言的函數(shù)都接受char*,void*還是后來(lái)才有的。C語(yǔ)言操作指針用的malloc和free,其實(shí)也是把他當(dāng)char*在看。所以當(dāng)你malloc了一個(gè)東西,然后cast成你需要的類(lèi)型,最后free掉,這一步cast存在不存在對(duì)于free能否正確執(zhí)行來(lái)說(shuō)是沒(méi)有區(qū)別的。

            但是事情到了C++就不一樣了。C++有繼承,有了繼承就有指針的隱式類(lèi)型轉(zhuǎn)換。于是看上面的代碼,我們new[]了一個(gè)指針是Derived*類(lèi)型的,然后隱式轉(zhuǎn)換到了Base*。最后我們拿他delete[],因?yàn)閐elete[]需要調(diào)用析構(gòu)函數(shù),但是Base*類(lèi)型的指針式不能正確計(jì)算出Derived數(shù)組的10個(gè)析構(gòu)函數(shù)需要的this指針的位置的,所以在這個(gè)時(shí)候,代碼就完蛋了(如果沒(méi)完蛋,那只是巧合)。

            為了兼容C語(yǔ)言,“new[]的指針需要delete[]”和“子類(lèi)指針可以轉(zhuǎn)父類(lèi)指針”的兩條規(guī)則成功的沖突到了一起。實(shí)際上,如果需要解決這種問(wèn)題,那類(lèi)型應(yīng)該怎么改呢?其實(shí)我們可以跟C#一樣引入Derived[]的這種指針類(lèi)型。這還是new[]出來(lái)的東西,C++里面也可以要求delete[],但是區(qū)別是他再也不能轉(zhuǎn)成Base[]了。只可惜,T[]這種類(lèi)型被C語(yǔ)言占用了,在函數(shù)參數(shù)類(lèi)型里面當(dāng)T*用。C語(yǔ)言浪費(fèi)語(yǔ)法罪該萬(wàn)死呀……

            待續(xù)

            posted on 2013-04-27 01:24 陳梓瀚(vczh) 閱讀(33278) 評(píng)論(37)  編輯 收藏 引用 所屬分類(lèi): 啟示

            評(píng)論:
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 02:02 | 溪流
            學(xué)習(xí)了  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 02:20 | sev
            板凳了..  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 03:43 | skyshaw
            “GetListLength_ [] c = x” 應(yīng)該是c吧  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 04:09 | 陳梓瀚(vczh)
            @skyshaw
            改了  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 06:22 | 蔡?hào)|赟
            mark,先下班回家看  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 06:25 | zhenqu
            不明覺(jué)厲。  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 07:48 | Richard Wei
            mark, 拜膜...  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a)[未登錄](méi) 2013-04-27 16:14 | 123
            小陳水平又提高了 呵呵  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 18:39 | tinro
            int &b; &b是int?當(dāng)然,你也可以說(shuō)C++不是C。int *b,和*b里面的*,不好簡(jiǎn)單作為一個(gè)含義來(lái)解釋。

            至于Haskell,從數(shù)學(xué)角度看很美好,從工程角度看很糟糕。  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 20:32 | clang
            看不懂他想噴什么,怎么辦?  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 20:37 | 不懂
            雖然很認(rèn)真的看了一遍,還是沒(méi)看懂“如何設(shè)計(jì)一門(mén)語(yǔ)言”的意思,只能說(shuō)樓主智商太高了  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a)[未登錄](méi) 2013-04-29 04:37 | raof01
            typedef int(__stdcall*f[10])(int(*a)(int, int));
            這個(gè)不是:“f是一個(gè)數(shù)組,數(shù)組返回了一個(gè)函數(shù)指針,……”。數(shù)組不會(huì)返回什么東西的啊。我覺(jué)得用typedef來(lái)解釋更清楚。上面的typedef等價(jià)于:
            typedef int (*A)(int, int);
            typedef int (__stdcall*F)(A);
            F[10];  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-05-03 23:21 | Quon
            想改變現(xiàn)狀的人做了個(gè)新的語(yǔ)言出來(lái),于是想出名的人有了可以噴的東西,評(píng)論家靠發(fā)明家吃飯.  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a)[未登錄](méi) 2013-05-15 22:13 | peakflys
            對(duì)于博主最后一個(gè)例子所說(shuō)的“但是Base*類(lèi)型的指針式不能正確計(jì)算出Derived數(shù)組的10個(gè)析構(gòu)函數(shù)需要的this指針的位置的”,請(qǐng)問(wèn)為什么不行呢?難點(diǎn)在哪?  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a)[未登錄](méi) 2013-05-15 22:17 | peakflys
            剛才構(gòu)造了一個(gè)例子,如下:
            #include <cstdio>

            class Base
            {
            public:
            Base(const int a=10) : a(a)
            {
            printf("Base constructor,this addr: %p\n",this);
            }
            virtual ~Base()
            {
            printf("Base destructor, a=%d, this addr: %p\n",a,this);
            }
            int a;
            };
            class Derive : public Base
            {
            public:
            Derive(const int a=20,const int b=20) : Base(a),b(b)
            {
            printf("Derive constructor,this addr: %p\n",this);
            }
            virtual ~Derive()
            {
            printf("Derive destructor, b=%d, this addr: %p\n",b,this);
            }
            int b;
            };
            int main(int argc, char* argv[])
            {
            Base *pb = new Derive[3];
            delete [] pb;
            printf("over\n");
            return 0;
            }
            特意在基類(lèi)和派生類(lèi)里各加了一個(gè)成員變量,來(lái)增大兩者可能的指針偏移。
            結(jié)果證明 delete 多態(tài)的指針后,內(nèi)存正常釋放,而每個(gè)派生類(lèi)對(duì)象的this指針也均無(wú)差錯(cuò)!
            編譯器:g++ (GCC) 4.4.6 20120305 (Red Hat 4.4.6-4)  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-05-16 00:04 | peakflys
            最后一個(gè)例子不是什么坑,只不過(guò)是ISO未定義的情況,其實(shí)要實(shí)現(xiàn)基類(lèi)指針正確找到派生類(lèi)this偏移的方式很多種,記下大小,加上固定偏移就是一種方法,當(dāng)然還有其他很多種方法,應(yīng)該沒(méi)什么特別的復(fù)雜,這里頂多告誡大家要慎用,因?yàn)槭菢?biāo)準(zhǔn)未定義的行為,crash or not crash,compiler decides it。建議博主修改之。  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-05-16 17:17 | weslee
            那么爛人永遠(yuǎn)都是爛人,不糾正自己的心態(tài)永遠(yuǎn)獲得不了任何有用的知識(shí),永遠(yuǎn)過(guò)那種月入一蛆的日子,用垃圾的語(yǔ)言痛苦的寫(xiě)一輩子沒(méi)價(jià)值的程序。  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-05-25 22:56 | Marvin
            我對(duì)一門(mén)好的語(yǔ)言的定義是
            你對(duì)語(yǔ)言的定位是什么,它恰當(dāng)?shù)慕鉀Q了你定位的問(wèn)題。

            個(gè)人覺(jué)得,如果按我的這種定義,只有2個(gè)語(yǔ)言很好。一個(gè)是C,一個(gè)是Ruby.

            如果你不懂,我給你舉個(gè)例子。C++號(hào)稱(chēng)要代替C成為主流開(kāi)發(fā)語(yǔ)言。它是要在C的基礎(chǔ)上加入面向?qū)ο蟆?山Y(jié)果是1.在關(guān)鍵的時(shí)候,還點(diǎn)用C而不是用C++,比如寫(xiě)操作系統(tǒng)。2. C++號(hào)稱(chēng)是要在C的基礎(chǔ)上加入面向?qū)ο螅@個(gè)目標(biāo)完成的怎么樣我不做評(píng)論,但它只加入了面向?qū)ο髥幔磕莻€(gè)++到底要加什么,恐怕連C++的設(shè)計(jì)者也不清楚。
            再一個(gè)例子,Java看到C++有問(wèn)題,號(hào)稱(chēng)叫C++--,它減掉了C++不好的東西了嗎?

            按我的理想,真正好語(yǔ)言還沒(méi)有出現(xiàn)。不然就不會(huì)出來(lái)這個(gè)多號(hào)稱(chēng)要改變這個(gè),改變那個(gè)的語(yǔ)言了。

            另外,我最常用的語(yǔ)言是C++, JavaScript. 也用過(guò)C#, Java, Obj-C, Ruby, PHP, Go等  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-05-25 22:57 | Marvin
            Sorry,上面的主頁(yè)地址寫(xiě)錯(cuò)了  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-05-28 19:19 | 陳梓瀚(vczh)
            @Marvin
            問(wèn)題解決的再好,語(yǔ)言設(shè)計(jì)的爛也沒(méi)用,看yacc,看antlr,只會(huì)讓你學(xué)起來(lái)痛苦。一門(mén)好的語(yǔ)言,首當(dāng)其沖就是“到處都沒(méi)有特例”,其次才考慮怎么良好的解決某些問(wèn)題——不過(guò)像C++這種語(yǔ)言其實(shí)可以解決大量的問(wèn)題的同時(shí)也解決的好,缺點(diǎn)是你要經(jīng)過(guò)大量的訓(xùn)練,很少人可以做到。學(xué)起來(lái)太難,好處就被抵消了。  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-06-03 21:09 | Marvin
            @陳梓瀚(vczh)
            C++有難度,我承認(rèn),但好像你沒(méi)有看明白我上面寫(xiě)的內(nèi)容。關(guān)鍵問(wèn)題是C++增加的這些難度是否是值得的,能有效解決本質(zhì)問(wèn)題的方法是最好的。  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-06-04 19:44 | 陳梓瀚(vczh)
            @Marvin
            這些難度100%是因?yàn)榧嫒軨語(yǔ)言+RAII而必須產(chǎn)生的。兼容C是值得的,RAII也是值得的,所以都是值得的。你無(wú)論去掉哪個(gè),要么就不兼容C,要么RAII就不完整。  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-07-13 05:51 | 幻の上帝
            @陳梓瀚(vczh)
            需要RAII是靜態(tài)語(yǔ)言+靜態(tài)類(lèi)型系統(tǒng)的原罪。
            WG21本可以不照抄ISO/IEC 9899的一堆translation phases,允許C++提供動(dòng)態(tài)語(yǔ)言的接口。結(jié)果還是抄了。為什么?怕無(wú)所適從?照顧習(xí)慣?市場(chǎng)原因?
            的確C的渣是C++設(shè)計(jì)最重大的包袱。但要說(shuō)100%就過(guò)了。舉幾個(gè)C++原創(chuàng)的渣設(shè)計(jì):
            1.new-expression的語(yǔ)法。
            2.復(fù)雜value category的必要性。
            3.混合不同的類(lèi)型系統(tǒng)導(dǎo)致無(wú)法交互。這樣說(shuō)可能太抽象,一個(gè)顯眼的問(wèn)題是模板為什么要獨(dú)立于類(lèi)型系統(tǒng)之外?操作符sizeof...為什么不能由庫(kù)實(shí)現(xiàn)?
            當(dāng)然關(guān)于類(lèi)型系統(tǒng)C有更多負(fù)資產(chǎn)。不過(guò)至少就這幾個(gè)例子不可能是C的過(guò)錯(cuò)。
              回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a)[未登錄](méi) 2013-07-13 09:53 | 陳梓瀚(vczh)
            @幻の上帝
            RAII的原罪顯然是內(nèi)存太貴,這個(gè)根本沒(méi)有別的原因,你怎么不說(shuō)GC是原罪呢。真實(shí)的

            我就不說(shuō)你那個(gè)亂七八糟的評(píng)論了,至少
            1:template怎么就在類(lèi)型系統(tǒng)以外了
            2:sizeof要用庫(kù)實(shí)現(xiàn)也不是不可能,至少在我現(xiàn)在在設(shè)計(jì)的語(yǔ)言里面就是可以的,不過(guò)這個(gè)東西是C的事情,C可沒(méi)有什么“用于偏特化的遞歸模板類(lèi)型”這一功能的,你讓他怎么做?  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a)[未登錄](méi) 2013-07-13 09:55 | 陳梓瀚(vczh)
            @幻の上帝
            我怕你看不明白再說(shuō)一句,為什么python打開(kāi)文件后也要close,還不是因?yàn)闆](méi)有RAII,你又不能等gc來(lái)日,只好自己動(dòng)手了嗎。  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-10-20 00:38 | 幻の上帝
            @陳梓瀚(vczh)
            GC當(dāng)然也是原罪。是不是必要呢,這是另外一回事了:不過(guò)我確信在靜態(tài)語(yǔ)言里強(qiáng)迫用戶(hù)使用GC更愚蠢,RAII在這里當(dāng)然是更好的解決方案。所以關(guān)于RAII的必要性你沒(méi)必要對(duì)我科普。
            根源在于向機(jī)器明確資源管理的邏輯是程序員的義務(wù)——現(xiàn)在顯然沒(méi)有AI能從根本上精確猜測(cè)用戶(hù)的意圖以自動(dòng)分清楚哪些資源是適合確定性析構(gòu)的,而哪些更適合延緩回收作為優(yōu)化。
            1.無(wú)論如何,C++的template不是type。無(wú)論從概念定義還是實(shí)用上來(lái)講template都和C++的type有顯著區(qū)別。盡管有些東西有共性,但在語(yǔ)言規(guī)則上做不到。就算不管這個(gè),還得考慮一些上下文:如C++11對(duì)T&&的類(lèi)型推導(dǎo)規(guī)則就因?yàn)門(mén)是或者不是類(lèi)型參數(shù)而有所不同。
            2.省略號(hào)沒(méi)被吃吧。是sizeof...不是sizeof。你試試看,打算重載幾個(gè)?別忘了模板非類(lèi)型參數(shù)和模板模板參數(shù)。


              回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-10-20 00:41 | 幻の上帝
            還有,這里我當(dāng)然沒(méi)有在說(shuō)C,C在這上面的無(wú)能顯而易見(jiàn)。
            我是在列舉C++原創(chuàng)的沒(méi)有照搬C的比較欠抽的設(shè)計(jì)。一些問(wèn)題不能在C++里解決說(shuō)明C++在這里的失敗——這也當(dāng)然不因?yàn)槟隳馨l(fā)明新語(yǔ)言而當(dāng)作解決了因此可以無(wú)視掉。

              回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-10-26 16:54 | Mr.Okay
            樓主您好!我是個(gè)語(yǔ)言設(shè)計(jì)方面的愛(ài)好者和學(xué)習(xí)者。您的文章真是寫(xiě)到我心里去了——我不是專(zhuān)業(yè)的,所以不可能像您這樣表達(dá)的這么清楚。對(duì)于C語(yǔ)言在語(yǔ)法方面的不一致,我一直想知道,下面這個(gè)例子是不是一個(gè)體現(xiàn):
            int a, *b;
            int* F(int a, int b);
            第一行里,它希望你把*b看為整數(shù),而第二行又希望你把int*當(dāng)作一種類(lèi)型。  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-10-27 22:04 | 陳梓瀚(vczh)
            @Mr.Okay
            所以說(shuō)這就是坑啊  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2013-11-01 10:18 | 幻の上帝
            不吱聲么。
            覺(jué)得sizeof...無(wú)厘頭的話(huà)就換一個(gè)比較能造成實(shí)際困難的例子。
            self-application lambda abstraction (λu.u u)在C艸里要寫(xiě)成什么類(lèi)型的?如何用一個(gè)name表示這個(gè)在類(lèi)型論意義上完全可以就是type但C艸就是不認(rèn)(也構(gòu)造不出)的東西?
            簡(jiǎn)而言之,除了在遞歸類(lèi)型上的不必要限制(函數(shù)不能聲明自身類(lèi)型為參數(shù))外,C艸聲稱(chēng):
            全稱(chēng)類(lèi)型(這里的模板)不是C++類(lèi)型。C++類(lèi)型不被量化。
            愚蠢的設(shè)計(jì)。
              回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2014-05-11 12:29 | xnature
            看到一半看不懂了

            “就是因?yàn)樗瑫r(shí)混用了兩種原則來(lái)設(shè)計(jì)語(yǔ)法”

            哪兩種?前文似乎只說(shuō)了一種啊  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2014-05-11 12:38 | xnature
            前文開(kāi)始C++定義中的function是什么東西啊?  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2014-05-11 12:54 | xnature
            最后一個(gè)例子在C++中正確的做法應(yīng)該是怎樣的?  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2014-05-12 07:39 | 陳梓瀚(vczh)
            @xnature
            std::function是C++11的一個(gè)類(lèi)  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2014-05-12 07:39 | 陳梓瀚(vczh)
            @xnature
            最后一個(gè)例子的正確做法當(dāng)然是Derived* ds = new Derived[10];  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2014-07-03 07:56 | dramforever
            沒(méi)人注意haskell? 樓主一定沒(méi)測(cè)試GetListLength
            函數(shù)第一個(gè)字母小寫(xiě)哦  回復(fù)  更多評(píng)論
              
            # re: 如何設(shè)計(jì)一門(mén)語(yǔ)言(一)&mdash;&mdash;什么是坑(a) 2014-10-26 19:00 | 幻の上帝
            借個(gè)地方。
            關(guān)于sizeof...那個(gè)嘛。。Google Groups上不去(本來(lái)應(yīng)該回復(fù)在跟Nicol Bolas的討論的)……
            http://en.wikipedia.org/wiki/Pure_type_system
            看來(lái)早幾年我又在發(fā)明無(wú)聊輪子了。
            (BTW,f*ck WG21/N1919。)
              回復(fù)  更多評(píng)論
              
            久久r热这里有精品视频| 国产高潮久久免费观看| 亚洲AV无码久久精品成人| 一本色道久久HEZYO无码| 国产一级做a爰片久久毛片| 久久久久久亚洲精品不卡| 亚洲国产精品久久久天堂| 99久久精品免费| 无码人妻少妇久久中文字幕蜜桃| 91精品国产色综合久久| 一级A毛片免费观看久久精品| 国产精品无码久久综合| 2021国产精品久久精品| 亚洲精品无码久久久久久| 色8激情欧美成人久久综合电| 久久精品国产亚洲网站| 精品熟女少妇a∨免费久久| 久久99精品久久久久久齐齐| 亚洲国产另类久久久精品黑人| 久久久女人与动物群交毛片| 久久久一本精品99久久精品66 | 亚洲欧洲精品成人久久奇米网| 日本WV一本一道久久香蕉| 国产欧美久久一区二区| 色综合久久久久| 久久99精品久久久久久动态图| 亚洲国产精品久久| 亚洲国产另类久久久精品| 久久久久久久人妻无码中文字幕爆 | 久久精品三级视频| 久久中文娱乐网| 亚洲国产精品无码久久SM| 久久精品国产亚洲7777| 久久最新精品国产| 久久夜色tv网站| 国产成人香蕉久久久久| 一级做a爰片久久毛片人呢| 青青草国产成人久久91网| 曰曰摸天天摸人人看久久久| 久久狠狠色狠狠色综合| 91久久成人免费|