編程語言的發(fā)展趨勢及未來方向-Anders_Hejlsberg_第1頁
編程語言的發(fā)展趨勢及未來方向-Anders_Hejlsberg_第2頁
編程語言的發(fā)展趨勢及未來方向-Anders_Hejlsberg_第3頁
編程語言的發(fā)展趨勢及未來方向-Anders_Hejlsberg_第4頁
編程語言的發(fā)展趨勢及未來方向-Anders_Hejlsberg_第5頁
已閱讀5頁,還剩38頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

1、編程語言的發(fā)展趨勢及未來方向一、歷史回顧及趨勢概述如果沒有特別說明,所有的文字都直接翻譯自Anders的演講,并使用我自己的口語習慣表達出來,對于Anders的口誤及反復等情況,必要時在譯文中自然也會進行忽略。為了方便理解,我也會將視頻中關(guān)鍵部分進行截圖,而某些代碼演示則會直接作為文章內(nèi)容發(fā)表。大家好,我是Anders Hejlsberg,現(xiàn)在是微軟的Technical Fellow,擔任C#編程語言的首席架構(gòu)師,也參與并領(lǐng)導.NET Framework以及各種語言的開發(fā)。我現(xiàn)在打算談一下實際上是我腦海中一些影響未來5到10年編程語言設(shè)計的內(nèi)容。比如C#或VB該怎么走,F(xiàn)#該怎么辦,這次演講主

2、要就是討論這些影響我們的東西。雖然主要內(nèi)容是談論未來的,但是我還是想先回顧一下歷史。你們有些人可能對這個產(chǎn)品有印象,這是我大約27年前的工作內(nèi)容,Turbo Pascal,這也是我進入這個領(lǐng)域的起點。我先在拿出這個東西是想展示當年寫程序的情況,然后可以討論目前究竟的發(fā)展到哪兒了。事實上,我現(xiàn)在的機器里正好有TURBO.COM文件,大約39K,嘿,現(xiàn)在還可以運行。我們現(xiàn)在來試著寫一點程序。先來創(chuàng)建的程序叫做Hello.pas(開始寫代碼)一個Pascal小程序?qū)懞昧?,我們來運行一下(出現(xiàn)編譯錯誤)啊噢,有地方我寫錯了這個特性在當年是個創(chuàng)新,它會自動打開編輯器,直接把我們帶去出錯的地方。嗯,我們現(xiàn)

3、在來糾正語法錯誤,把雙引號改成單引號。再運行一下,現(xiàn)在成功了,(觀眾掌聲)呵呵,謝謝,謝謝。事實上,在27年后這個程序還能在這臺機器上運行還真是挺神奇的?,F(xiàn)在,我們來看一下,從那時算起硬件已經(jīng)發(fā)展了嗯,我那時寫Pascal的機器是Z-80,擁有48K內(nèi)存。從那時算起,我現(xiàn)在這臺機器已經(jīng)有大約10萬倍的外部存儲容量,1萬倍的內(nèi)存大小,CPU速度也有大約1000倍的提高。但是如果你關(guān)注一下目前的軟件過去27年里編程語言到底進步了多少?呵呵,有趣的是如果你仔細觀察這些代碼,會發(fā)現(xiàn)C#還比Turbo Pascal的版本多一行。這也給我們帶來了一些值得關(guān)注的東西。首先,編程語言的發(fā)展非常緩慢。期間當然出

4、現(xiàn)了一些東西,例如面向?qū)ο蟮鹊?,但是遠沒有好上1000倍。另一方面,你可能會想,那么這些努力都到哪里去了呢?事實上這些努力沒有體現(xiàn)在編程語言上,而是出現(xiàn)在框架及工具等方面了。如果你關(guān)注如今我們使用的框架,它們的體積的確有1000倍的增長。例如當年Turbo Pascal所帶的框架大約有,比如說100個功能,而現(xiàn)在的.NET Framework里則有一萬個類,十萬個方法,的確有1000倍的增長。與此類似,如果你觀察現(xiàn)在的IDE,我們現(xiàn)在已經(jīng)有了無數(shù)強大的功能,例如語法提示,重構(gòu),調(diào)試器,探測器等等,這方面的新東西有很多。與此相比,編程語言的改進的確很不明顯。另一方面,如.NET,Java等框架的

5、重要性提高了許多。而編程語言往往都傾向于構(gòu)建于現(xiàn)有的工具上,而不會從頭寫起。現(xiàn)在出現(xiàn)的編程語言,例如F#,如果你關(guān)注Java領(lǐng)域那么還有Scala,Clojure等等,它們都是基于現(xiàn)有框架構(gòu)建的?,F(xiàn)在已經(jīng)有太多東西可以直接利用了,每次從頭開始的代價實在太高。還有件事,便是在過去5、60年的編程歷史中,我們都不斷地提高抽象級別,我們都在不斷地讓編程語言更有表現(xiàn)力,讓我們可以用更少的代碼完成更多的工作。我們一開始先使用匯編,然后使用面向過程的語言,例如Pascal和C,然后便是面向?qū)ο笳Z言,如C+,隨后就進入了托管時代受托管的執(zhí)行環(huán)境,例如.NET,Java,它們的主要特性有自動的垃圾收集,類型安

6、全等等。我目前還沒有看出這樣的趨勢有停止的跡象,因此我們還會看到抽象級別越來越高的語言,而語言的設(shè)計者則必須理解并預測下一個抽象級別是什么樣子的。我認為,現(xiàn)在影響力較大的趨勢主要有3種。首先,我們會越來越多地使用聲明式的編程風格。這里我主要會提到例如DSL(Domain Specific Language,領(lǐng)域特定語言)以及函數(shù)式編程。然后在過去的五年里,我發(fā)現(xiàn)對于動態(tài)語言的研究變得非常火熱,其中對我們產(chǎn)生重大影響的無疑是動態(tài)語言所擁有的良好的元編程能力,還有一些非常有趣的東西,例如JavaScript引擎的發(fā)展。然后便是并發(fā)編程,無論我們愿不愿意,多核的產(chǎn)生都在迫使我們不得不重視并發(fā)編程。有

7、一點值得一提,那便是隨著語言的發(fā)展,原本的編程語言分類方式也要有所改變了。以前我們經(jīng)常說面向?qū)ο笳Z言,動態(tài)語言或是函數(shù)式語言。但是我們現(xiàn)在發(fā)現(xiàn),這些邊界變得越來越模糊,經(jīng)常會互相學習各自的范式。靜態(tài)語言中出現(xiàn)了動態(tài)類型,動態(tài)語言里也出現(xiàn)了靜態(tài)能力,而如今所有主要的編程語言都受到函數(shù)式語言的影響。因此,一個越來越明顯的趨勢是“多范式程序設(shè)計語言”。在接下來的部分中,我將深入討論以上提到的這些內(nèi)容。二、聲明式編程與DSL這是Anders Hejlsberg(不用介紹這是誰了吧)在比利時TechDays 2010所做的開場演講。由于最近我在博客上關(guān)于語言的討論比較多,出于應景,也打算將Anders的

8、演講完整地聽寫出來。在上一部分中,Anders指出語言本身在過去的數(shù)十年里并沒有明顯的發(fā)展,并給出了他眼中編程語言發(fā)展趨勢的預測。在現(xiàn)在的第2部分中,Anders將闡述聲明式編程的理念及DSL,并演示C#中一種內(nèi)部DSL的形式:LINQ。如果沒有特別說明,所有的文字都直接翻譯自Anders的演講,并使用我自己的口語習慣表達出來,對于Anders的口誤及反復等情況,必要時在譯文中自然也會進行忽略。為了方便理解,我也會將視頻中關(guān)鍵部分進行截圖,而某些代碼演示則會直接作為文章內(nèi)容發(fā)表。(聽寫開始,接上篇)這里先從聲明式(Declarative)編程談起。 目前我們在編寫軟件時大量使用的是命令式(Im

9、perative)編程語言,例如C#,Java或是C+等等。這些語言的特征在于,寫出的代碼除了表現(xiàn)出“什么(What)”是你想做的事情之外,更多的代碼則表現(xiàn)出實現(xiàn)的細節(jié),也就是“如何(How)”完成工作。這部分代碼有時候多到掩蓋了我們原來問題的解決方案。比如,你會在代碼里寫for循環(huán),if語句,a等于b,i加一等等,這體現(xiàn)出機器是如何處理數(shù)據(jù)。首先,這種做法讓代碼變得冗余,而且它也很難讓執(zhí)行代碼的基礎(chǔ)設(shè)施更聰明地判斷該如何去執(zhí)行代碼。當你寫出這樣的命令是代碼,然后把編譯后的中間語言交給虛擬機去執(zhí)行,此時虛擬機并沒有多少空間可以影響代碼的執(zhí)行方式,它只能根據(jù)指令一條一條老老實實地去執(zhí)行。例如,我

10、們現(xiàn)在想要并行地執(zhí)行程序就很困難了,因為更高層次的一些信息已經(jīng)丟失了。這樣,我們只能在代碼里給出“How”,而不能體現(xiàn)出“What”的信息。 有多種方式可以將“What”轉(zhuǎn)化為更為“聲明式”的編程風格,我們只要能夠在代碼中體現(xiàn)出更多“What”,而不是“How”的信息,這樣執(zhí)行環(huán)境便可以更加聰明地去適應當前的執(zhí)行要求。例如,它可以決定投入多少CPU進行計算,你的當前硬件是什么樣的,等等。 我之前提到過,現(xiàn)在有兩種比較重要的成果,一是DSL(Domain Specific Language,領(lǐng)域特定語言),另一個則是函數(shù)式編程。 其實DSL不是什么新鮮的玩意兒,我們平時一直在用類似的東西,比如,

11、SQL,CSS,正則表達式,有的可能更加專注于一個方面,例如Mathematica,LOGO等等。這些語言的目標都是特定的領(lǐng)域,與之相對的則是GPPL(General Purpose Programming Language,通用目的編程語言)。 對于DSL而言其實并沒有一個明確的定義,在這里我也不打算為它下個定義,例如UML甚至根本沒有特定的語法。不過我這里會談一些我覺得比較重要的東西。 Martin Fowler提出DSL應該分為外部DSL及內(nèi)部DSL兩種,我認為這種劃分方式還是比較有意義的。外部DSL是自我包含的語言,它們有自己特定語法、解析器和詞法分析器等等,它往往是一種小型的編程語言

12、,甚至不會像GPPL那樣需要源文件。與之相對的則是內(nèi)部DSL。內(nèi)部DSL其實更像是種別稱,它代表一類特別API及使用模式。這里我會給你們看一些示例。 這些是我們平時會遇到的一些外部DSL,如這張幻燈片上表現(xiàn)的XSLT,SQL或是Unix腳本。外部DSL的特點是,你在構(gòu)建這種DSL時,其實扮演的是編程語言設(shè)計者的角色,這個工作并不會交給普通人去做。外部DSL一般會直接針對特定的領(lǐng)域設(shè)計,而不考慮其他東西。James Gosling曾經(jīng)說過這樣的話,每個配置文件最終都會變成一門編程語言。你一開始可能只會用它表示一點點東西,然后慢慢你便會想要一些規(guī)則,而這些規(guī)則則變成了表達式,可能你還會定義變量,進

13、行條件判斷等等。而最終它就變成了一種奇怪的編程語言,這樣的情況屢見不鮮。 事實上,現(xiàn)在有一些公司也在關(guān)注DSL的開發(fā)。例如以前在微軟工作的Charles Simonyi提出了Intentional Programming的概念,還有一個叫做JetBrains的公司提供一個叫做MPS(Meta Programming System)的產(chǎn)品。最近微軟也提出了自己的Oslo項目,而在Eclipse世界里也有個叫做Xtext的東西,所以其實在這方面現(xiàn)在也有不少人在嘗試。 我在觀察外部DSL時,往往會關(guān)注它的語法到底提供了多少空間,例如一種XML的方言,利用XML方言的好處在于有不少現(xiàn)成的工具可用,這樣

14、可以更快地定義自己的語法。 而內(nèi)部DSL,正像我之前說的那樣,它其實只是一系列特別的API及使用模式的別稱。這里則是一些LINQ查詢語句,Ruby on Rails以及jQuery代碼。內(nèi)部DSL的特點是,它其實只是一系列API,但是你可以“假裝”它們一種DSL。內(nèi)部DSL往往會利用一些“流暢化”的技巧,例如像這里的LINQ或jQuery那樣把一些方法通過“點”連接起來。有些則利用了元編程的方式,如這里的Ruby on Rails就涉及到了一些元編程。這種DSL可以訪問語言中的代碼或變量,以及利用如代碼補全,重構(gòu)等母語言的所有特性。 現(xiàn)在我會花幾分鐘時間演示一下我所創(chuàng)建的DSL,也就是LINQ

15、。我相信你們也已經(jīng)用過不少LINQ了,不過這里我還是快速的展示一下我所表達的更為“聲明式”的編程方式。public class Product public int ProductID get; set; public string ProductName get; set; public string CategoryName get; set; public int UnitPrice get; set; public static List<Product> GetProducts() /* . */ public partial class _Default : Syste

16、m.Web.UI.Page protected void Page_Load(object sender, EventArgs e) List<Product> products = Product.GetProducts(); List<Product> result = new List<Product>(); foreach (Product p in products) if (p.UnitPrice > 20) result.Add(p); GridView1.DataSource = result; GridView1.DataBind()

17、; 這里有許多Product對象,那么現(xiàn)在我要篩選出所有單價大于20的那些, 再把他們顯示在一個GridView中。傳統(tǒng)的做法就是這樣,我先得到所有的Product對象,然后foreach遍歷每個對象,再判斷每個對象的單價,最終把數(shù)據(jù)綁定到GridView里。運行這個程序(打開頁面)這就是就能得到結(jié)果。 好,那么現(xiàn)在我要做一些稍微復雜的事情??赡芪也皇且故締蝺r超過20的Product對象,而是要查看每個分類中究竟有多少個單價超過20的對象,然后根據(jù)數(shù)量進行排序。如果不用DSL完成這個工作,那么我可能會先定義一個對象來表示結(jié)果:class Grouping public string Cate

18、goryName get; set; public int ProductCount get; set; 這是個表示分組的對象,用于保存分類的名稱和產(chǎn)品數(shù)量。然后我們就會寫一些十分丑陋的代碼:Dictionary<string, Grouping> groups = new Dictionary<string, Grouping>();foreach (Product p in products) if (p.UnitPrice >= 20) if (!groups.ContainsKey(p.CategoryName) Grouping r = new Grou

19、ping(); r.CategoryName = p.CategoryName; r.ProductCount = 0; groupsp.CategoryName = r; groupsp.CategoryName.ProductCount+; List<Grouping> result = new List<Grouping>(groups.Values);result.Sort(delegate(Grouping x, Grouping y) return x.ProductCount > y.ProductCount ? -1 : x.ProductCoun

20、t < y.ProductCount ? 1 : 0;);我先創(chuàng)建一個新的字典,用于保存分類名稱到分組的對應關(guān)系。然后我遍歷每個Product對象,對于每個單價大于20的對象,如果字典中還沒有保存對應的分組則創(chuàng)建一個,然后將數(shù)量加一。然后為了排序,我調(diào)用Sort方法,于是我要提供一個委托作為排序方法,然后blablablabla執(zhí)行之后(打開頁面)我自然可以得到想要的結(jié)果。 但是,首先這些代碼寫起來需要花費一些時間,很顯然。然后仔細觀察,你會發(fā)現(xiàn)這寫代碼幾乎都是在表示“How”,而“What”基本已經(jīng)丟失了。假設(shè)我離開了,現(xiàn)在新來了一個程序員要維護這段代碼,他會需要一點時間才能完整理解這

21、段代碼,因為他無法直接看清代碼的目標。 不過如果這里我們使用DSL,也就是LINQ,就像這樣:var result = products .Where(p => p.UnitPrice >= 20) .GroupBy(p => p.CategoryName) .OrderByDescending(g => g.Count() .Select(g => new CategoryName = g.Key, ProductCount = g.Count() );products先調(diào)用Whereblablabla再GroupBy等等。由于我們這里可以使用DSL來表示高階的

22、術(shù)語,用以體現(xiàn)我們想做的事情。于是這段代碼則更加關(guān)注于“What”而不是“How”。我這里不會明確地指示我想要過濾的方式,我也不會明確地說我要建立字典和分類,這樣基礎(chǔ)結(jié)構(gòu)就可以聰明地,或者說更加聰明地去確定具體的執(zhí)行方式。你可能比較容易想到我們可以并行地執(zhí)行這段代碼,因為我沒有顯式地指定做事方式,我只是表示出我的意圖。 我們打開頁面(打開頁面)很顯然我們得到了相同的結(jié)果。 這里比較有趣的是,內(nèi)部DSL是如何設(shè)計進C#語法中的,為此我們?yōu)镃# 3.0添加了一系列的特性,例如Lambda表達式,擴展方法,類型推斷等等。這些特性統(tǒng)一起來之后,我們就可以設(shè)計出更為豐富的API,組合之后便成為一種內(nèi)部D

23、SL,就像這里的LINQ查詢語言。 除了使用API的形式之外,我們還可以這樣做:var result = from p in products where p.UnitPrice >= 20 group p by p.CategoryName into g orderby g.Count() descending select new CategoryName = g.Key, ProductCount = g.Count() ;編譯器會簡單地將這種形式轉(zhuǎn)化為前一種形式。不過,這里我認為有意思的地方在于,你完全可以創(chuàng)建一門和領(lǐng)域編程語言完全無關(guān)的語法,然后等這種語法和API變得流行且豐富

24、起來之后,再來創(chuàng)一種新的表現(xiàn)形式,就如這里的LINQ查詢語法。我頗為中意這種語言設(shè)計的交流方式。 OK,現(xiàn)在我們回到下面的內(nèi)容。三、函數(shù)式編程這是Anders Hejlsberg(不用介紹這是誰了吧)在比利時TechDays 2010所做的開場演講。由于最近我在博客上關(guān)于語言的討論比較多,出于應景,也打算將Anders的演講完整地聽寫出來。在上一部分中,Anders闡述了他眼中聲明式編程的理念及DSL,并演示C#中一種內(nèi)部DSL的形式:LINQ。在這一部分中,Anders談及了聲明式編程的另一個重要組成部分:函數(shù)式編程,并使用.NET平臺上的函數(shù)式編程語言F#進行了演示。如果沒有特別說明,所有

25、的文字都直接翻譯自Anders的演講,并使用我自己的口語習慣表達出來,對于Anders的口誤及反復等情況,必要時在譯文中自然也會進行忽略。為了方便理解,我也會將視頻中關(guān)鍵部分進行截圖,而某些代碼演示則會直接作為文章內(nèi)容發(fā)表。(聽寫開始,接上篇)關(guān)于聲明式編程的還有一部分重要的內(nèi)容,那便是函數(shù)式編程。函數(shù)式編程已經(jīng)有很長時間的歷史了,當年LISP便是個函數(shù)式編程語言。除了LISP以外我們還有其他許多函數(shù)式編程語言,如APL、Haskell、Scheme、ML等等。關(guān)于函數(shù)式編程在學術(shù)界已經(jīng)有過許多研究了,在大約5到10年前許多人開始吸收和整理這些研究內(nèi)容,想要把它們?nèi)谌敫鼮橥ㄓ玫木幊陶Z言?,F(xiàn)在的

26、編程語言,如C#、Python、Ruby、Scala等等,它們都受到了函數(shù)式編程語言的影響。我想在這里先花幾分鐘時間簡單介紹一下我眼中的函數(shù)式編程語言。我發(fā)現(xiàn)很多人聽說過函數(shù)式編程語言,但還不十分清楚它們和普通的命令式編程語言究竟有什么區(qū)別。如今我們在使用命令式編程語言寫程序時,我們經(jīng)常會寫這樣的語句,嗨,x等于x加一,此時我們大量依賴的是狀態(tài),可變的狀態(tài),或者說變量,它們的值可以隨程序運行而改變。 可變狀態(tài)非常強大,但隨之而來的便是叫做“副作用”的問題。在使用可變狀態(tài)時,你的程序則會包含副作用,比如你會寫一個無需參數(shù)的void方法,然后它會根據(jù)你的調(diào)用次數(shù)或是在哪個線程上進行調(diào)用對程序產(chǎn)生影

27、響,因為void方法會改變程序內(nèi)部的狀態(tài),從而影響之后的運行效果。 而在函數(shù)式編程中則不會出現(xiàn)這個情況,因為所有的狀態(tài)都是不可變的。你可以聲明一個狀態(tài),但是不能改變這個狀態(tài)。而且由于你無法改變它,所以在函數(shù)式編程中不需要變量。事實上對函數(shù)式編程的討論更像是數(shù)學、公式,而不像是程序語句。如果你把x = x + 1這句話交給一個程序員看,他會說“啊,你在增加x的值”,而如果你把它交給一個數(shù)學家看,他會說“嗯,我知道這不是true”。然而,如果你給他看這條語言,他會說“啊,y等于x加一,就是把x + 1的計算結(jié)果交給y,你是為這個計算指定了一個名字”。這時候在思考時就是另一種方式了,這里y不是一個變

28、量,它只是x + 1的名稱,它不會改變,永遠代表了x + 1。 所以在函數(shù)式編程語言中,當你寫了一個函數(shù),接受一些參數(shù),那么當你調(diào)用這個函數(shù)時,影響函數(shù)調(diào)用的只是你傳進去的參數(shù),而你得到的也只是計算結(jié)果。在一個純函數(shù)式編程語言中,函數(shù)在計算時不會對進行一些神奇的改變,它只會使用你給它的參數(shù),然后返回結(jié)果。在函數(shù)式編程語言中,一個void方法是沒有意義的,它唯一的作用只是讓你的CPU發(fā)熱,而不能給你任何東西,也不會有副作用。當然現(xiàn)在你可能會說,這個CPU發(fā)多少熱也是一個副作用,好吧,不過我們現(xiàn)在先不討論這個問題。這里的關(guān)鍵在于,你解決問題的方法和以前大不一樣了。我這里還是用代碼來說明問題。使用函

29、數(shù)式語言寫沒有副作用的代碼,就好比在Java或C#中使用final或是readonly的成員。 例如這里,我們有一個Point類,構(gòu)造函數(shù)接受x和y,還有一個MoveBy方法,可以把一個點移動一些位置。 在傳統(tǒng)的命令式編程中,我們會改變Point實例的狀態(tài),這么做在平時可能不會有什么問題。但是,如果我把一個Point對象同時交給3個API使用,然后我修改了Point,那么如何才能告訴它們狀態(tài)改變了呢?可能我們可以使用事件,blablabla,如果我們沒有事件,那么就會出現(xiàn)那些不愉快的副作用了。 那么使用函數(shù)式編程的形式寫代碼,你的Point類還是可以包含狀態(tài),例如x和y,不過它們是readon

30、ly的,一旦初始化以后就不能改變了。MoveBy方法不能改變Point對象,它只能創(chuàng)建一個新的Point對象并返回出來。這就是一個創(chuàng)建新Point對象的函數(shù),不是嗎?這樣就可以讓調(diào)用者來決定是使用新的還是舊的Point對象,但這里不會有產(chǎn)生副作用的情況出現(xiàn)。 在函數(shù)式編程里自然不會只有Point對象,例如我們會有集合,如Dictionary,Map,List等等,它們都是不可變的。在函數(shù)式編程中,當我們向一個List里添加元素時,我們會得到一個新的List,它包含了新增的元素,但之前的List依然存在。所以這些數(shù)據(jù)結(jié)構(gòu)的實現(xiàn)方式是有根本性區(qū)別的,它們的內(nèi)部結(jié)構(gòu)會設(shè)法讓這類操作變的盡可能高效。

31、在函數(shù)式編程中訪問狀態(tài)是十分安全的,因為狀態(tài)不會改變,我可以把一個Point或List對象交給任意多的地方去訪問,完全不用擔心副作用。函數(shù)式編程的十分容易并行,因為我在運行時不會修改狀態(tài),因此無論多少線程在運行時都可以觀察到正確的狀態(tài)。兩個函數(shù)完全無關(guān),因此它們是并行還是順序地執(zhí)行便沒有什么區(qū)別了。我們還可以有延遲計算,可以進行Memorization,這些都是函數(shù)式編程中十分有趣的方面。 你可能會說,那么我們?yōu)槭裁床欢加眠@種方法來寫程序呢?嗯,最終,就像我之前說的那樣,我們不能只讓CPU發(fā)熱,我們必須要把計算結(jié)果表現(xiàn)出來。那么我們在屏幕上打印內(nèi)容時,或者把數(shù)據(jù)寫入文件或是Socket時,其實

32、就產(chǎn)生了副作用。因此真實世界中的函數(shù)式編程,往往都是把純粹的部分進行隔離,或是進行更細致的控制。事實上也不會有真正純粹的函數(shù)式編程語言,它們都會帶來一定的副作用或是命令式編程的能力。但是,它們默認是函數(shù)式的,例如在函數(shù)式編程語言中,所有東西默認都是不可變的,你必須做些額外的事情才能使用可變狀態(tài)或是產(chǎn)生危險的副作用。此時你的編程觀念便會有所不同了。我們在自己的環(huán)境中開發(fā)出了這樣一個函數(shù)式編程語言,F(xiàn)#,已經(jīng)包含在VS 2010中了。F#誕生于微軟劍橋研究院,由Don Syme提出,他在F#上已經(jīng)工作了5到10年了。F#使用了另一個函數(shù)式編程語言O(shè)Caml的常見核心部分,因此它是一個強類型語言,并

33、支持一些如模式匹配,類型推斷等現(xiàn)代函數(shù)式編程語言的特性。在此之上,F(xiàn)#又增加了異步工作流,度量單位等較為前沿的語言功能。 而F#最為重要的一點可能是,在我看來,它是第一個和工業(yè)級的框架和工具集,如.NET和Visual Studio,有深入集成的函數(shù)式編程語言。F#允許你使用整個.NET框架,它和C#也有類似的執(zhí)行期特征,例如強類型,而且都會生成高效的代碼等等。我想,現(xiàn)在應該是展示一些F#代碼的時候了。首先我想先從F#中我最喜歡的特性講起,這是個F#命令行(打開命令行窗口以及一個F#源文件)F#包含了一個交互式的命令行,這允許你直接輸入代碼并執(zhí)行。例如輸入5x等于5然后x顯示出x的值是5。然后

34、讓sqr x等于x乘以x,于是我這里定義了一個簡單的函數(shù),名為sqr。于是我們就可以計算sqr 5等于25,sqr 10等于100。 F#的使用方式十分動態(tài),但事實上它是一個強類型的編程語言。我們再來看看這里。這里我定義了一個計算平方和的函數(shù)sumSquares,它會遍歷每個列表中每個元素,平方后再把它們相加。讓我先用命令式的方式編寫這個函數(shù),再使用函數(shù)式的方式,這樣你可以看出其中的區(qū)別。let sumSquaresI l = let mutable acc = 0 for x in l do acc <- acc + sqr x acc這里先是命令式的代碼,我們先創(chuàng)建一個累加器acc為

35、0,然后遍歷列表l,把平方加到acc中,然后最后我返回acc。有幾件事情值得注意,首先為了創(chuàng)建一個可變的狀態(tài),我必須顯式地使用mutable進行聲明,在默認情況下這是不可變的。還有一點,這段代碼里我沒有提供任何的類型信息。當我把鼠標停留在方法上時,就會顯示sumSquaresI方法接受一個int序列作為參數(shù)并返回一個int。你可能會想int是哪里來的,嗯,它是由類型推斷而來的。編譯器從這里的0發(fā)現(xiàn)acc必須是一個int,于是它發(fā)現(xiàn)這里的加號表示兩個int的相加,于是sqr函數(shù)返回的是個int,再接下來blablabla最終它發(fā)現(xiàn)這里到處都是int。如果我把這里修改為浮點數(shù)0.0,鼠標再停留一下

36、,你就會發(fā)現(xiàn)這個函數(shù)接受和返回的類型都變成float了。所以這里的類型推斷功能十分強大,也十分方便?,F(xiàn)在我可以選擇這個函數(shù),讓它在命令行里執(zhí)行,然后調(diào)用sumSquaresI,提供1到100的序列,就能得到結(jié)果了。let rec sumSquaresF l = match l with | -> 0 | h : t -> sqr h + sumSquaresF t那么現(xiàn)在我們來換一種函數(shù)式的風格。這里是另一種寫法,可以說是純函數(shù)式的實現(xiàn)方式。如果你去理解這段代碼,你會發(fā)現(xiàn)有不少數(shù)學的感覺。這里我定義了sumSqauresF函數(shù),輸入一個l列表,然后使用下面的模式去匹配l。如果它為空

37、,則結(jié)果為0,否則把列表匹配為頭部和尾部,然后便將頭部的平方和尾部的平方和相加。 你會發(fā)現(xiàn),在計算時我不會去改變?nèi)魏我粋€變量的值,我只是創(chuàng)建新的值。我這里會使用遞歸,就像在數(shù)學里我們經(jīng)常使用遞歸,把一個公式分解成幾個變化的形式,以此進行遞歸的定義。在編程時我們也使用遞歸的做法,然后編譯器會設(shè)法幫我們轉(zhuǎn)化成尾遞歸或是循環(huán)等等。 于是我們便可以執(zhí)行sumSquaresF函數(shù),也可以得到相同的結(jié)果。當然實際上可能你并不會像之前這樣寫代碼,你可能會使用高階函數(shù):let sumSquares l = Seq.sum (Seq.map (fun x -> x * x) l )例如這里,我只是把函數(shù)x

38、乘以x映射到列表上,然后相加。這樣也可以得到相同的結(jié)果,而且這可能是更典型的做法。我這里只是想說明,這個語言在編程時可能會給你帶來完全不同的感受,雖然它的執(zhí)行期特征和C#比較接近。 這便是關(guān)于F#的內(nèi)容。四、動態(tài)語言這是Anders Hejlsberg(不用介紹這是誰了吧)在比利時TechDays 2010所做的開場演講。由于最近我在博客上關(guān)于語言的討論比較多,出于應景,也打算將Anders的演講完整地聽寫出來。在上一部分中,Anders談及了聲明式編程的另一個重要組成部分:函數(shù)式編程,并使用.NET平臺上的函數(shù)式編程語言F#進行了演示。在這一部分中,Anders討論了動態(tài)語言及JavaScr

39、ipt的相關(guān)內(nèi)容,“動態(tài)性”也是Anders眼中編程語言的發(fā)展趨勢之一。如果沒有特別說明,所有的文字都直接翻譯自Anders的演講,并使用我自己的口語習慣表達出來,對于Anders的口誤及反復等情況,必要時在譯文中自然也會進行忽略。為了方便理解,我也會將視頻中關(guān)鍵部分進行截圖,而某些代碼演示則會直接作為文章內(nèi)容發(fā)表。(聽寫開始,接上篇)我下面繼續(xù)要講的是動態(tài)語言,這也是我之前提到的三種趨勢之一。 我還是嘗試著去找到動態(tài)語言的定義,但是你也知道一般地說,動態(tài)語言是一些不對編譯時和運行時進行嚴格區(qū)分的語言。這不像一些靜態(tài)編程語言,比如C#,你先進行編譯,然后會得到一些編譯期錯誤,稍后再執(zhí)行,而對于

40、動態(tài)語言來說這兩個階段便混合在一起了。我們都熟悉一些動態(tài)語言,比如JavaScript,Python,Ruby,LISP等等。 動態(tài)語言有一些優(yōu)勢,而靜態(tài)語言也有著另一些優(yōu)勢,這也是兩個陣營爭論多年的內(nèi)容。老實講,我認為結(jié)果不是兩者中的任意一個,它們都有各自十分重要的優(yōu)點,而長期來看,我認為結(jié)果應該是兩者的雜交產(chǎn)物,我認為在語言發(fā)展中也可以看到這樣的趨勢,這兩部分內(nèi)容正在合并。 許多人認定動態(tài)語言執(zhí)行起來很慢,也沒有類型安全等等。我想在這里觀察并比較一下,究竟是什么原因會讓靜態(tài)語言和動態(tài)語言在這方面有不同的性質(zhì)。這里有一段有趣的代碼,它的語法在JavaScript和C#里都是正確的,這樣我們便

41、能比較兩種語言是如何處理這段代碼的。 首先我們把它看作是一段C#代碼,它只是用for循環(huán)把一堆整數(shù)相加,你肯定不會這么做,這只是一個示例。在C#中,當我們使用var關(guān)鍵字時,它表示“請為我推斷這里的類型”,所以在這里a和i的類型都是int。 這斷代碼在執(zhí)行的時候,這兩個值都是32位整數(shù),而for循環(huán)只是簡單的使用ADD指令即可,執(zhí)行起來自然效率很高。 但如果從JavaScript或是動態(tài)語言的角度來看或者說對于動態(tài)類型的語言來說,var只代表了“一個值”,它可以是任意類型,我們不知道它究竟是什么。所以當我們使用var a或var i時,我們只是定義了兩個值,其中包含了一個“類型”標記,表明在運

42、行時它是個什么類型。在這里它是一個int,因此包含了存儲int值的空間。但有些時候,例如要存儲一個double值,那么可能便需要更多的空間,還可能是一個字符串,于是便包含一個引用。 所以兩者的區(qū)別之一便是,表示同樣的值在動態(tài)語言中會有一些額外的開銷,代價較高。而在如今的CPU中,“空間”便等于“速度”,所以較大的值便需要較長時間進行處理,這里便損失了一部分效率。 在JavaScript中,我們?nèi)绻幚韆加i,那么便不僅僅是一個ADD指令。首先它必須查看兩個變量中的類型標記,然后根據(jù)類型選擇合適的相加操作。于是再去加載兩個值,然后再進行加法操作。這里還需要進行越界檢查,因為在JavaScrip

43、t中一旦越界了便要使用double,等等。很明顯在這里也有許多開銷。一般來說,動態(tài)語言是使用解釋器來執(zhí)行的,因此還有一些解釋器需要的二進制碼。你把這些開銷全部加起來以后,便會發(fā)現(xiàn)執(zhí)行代碼時需要10倍到100倍的開銷。 不過由于近幾年來出現(xiàn)的一些動態(tài)虛擬機或引擎,目前這些情況改善了許多。比方說,這是傳統(tǒng)的情況(上圖左),如在IE 6或IE 7里使用的非常緩慢的解釋器。目前的情況是,大部分的JavaScript引擎使用了JIT編譯器(上圖中),于是便省下了解釋器的開銷,這樣性能損失便會減小至3到10倍。而在過去的兩三年間,JIT編譯器也變得越來越高效,瀏覽器中新一代的適應性JIT編譯器(上圖右),

44、如TraceMonkey,V8,還有如今微軟在IE 9中使用的Chakra引擎。這種適應性的JIT編譯器使用了一部分有趣的技術(shù),如Inline Caching、Type Specialization、Hidden Classes、Tracing等等,它們可以將開銷降低至2到3倍的范圍內(nèi),這種效率的提升可謂十分神奇。 在我看來,JavaScript引擎可能已經(jīng)接近了性能優(yōu)化的極限,我們在效率上可以提升的空間已經(jīng)不多。不過我同樣認為,如今JavaScript語言的性能已經(jīng)足夠快了,完全有能力統(tǒng)治Web客戶端。 有人認為,JavaScript從來不是一種適合進行大規(guī)模編程的語言。如今也有一些有趣的工

45、具,如Google Web Tookit,在微軟Nikhil Kothari也創(chuàng)建了Script#,讓你可以編寫C#或Java代碼,然后將代碼編譯成JavaScript,這就像是將JavaScript當作是一種中間語言。Google Wave的所有代碼都用GWT寫成,它的團隊堅持認為用JavaScript不可能完成這樣的工作,因為復雜度實在太高了。如今在這方面還有一些有趣的開發(fā)成果,我不清楚什么時候會結(jié)束。不過我認為,這些都不算是大規(guī)模的JavaScript開發(fā)方案,而編寫C#或Java代碼再生成JavaScript的方式也不能算是完全正確的做法。我們可以關(guān)注這方面的走向。 在.NET 4.0

46、的運行時進行動態(tài)編程時,我們引入了一個新功能:動態(tài)語言運行時??梢赃@樣理解,CLR的目的是為靜態(tài)類型的編程語言提供一個統(tǒng)一的框架或編程模型,而DLR便是在.NET平臺上為動態(tài)語言提供了統(tǒng)一的編程模型。CLR本身已經(jīng)有一些支持動態(tài)編程能力,如反射,Emit等等。不過在.NET上實現(xiàn)動態(tài)語言的時候,總會一遍又一遍地去實現(xiàn)某些功能,還有如動態(tài)語言如何與靜態(tài)語言進行交互,這些都由DLR來提供。DLR的特性包含了,如表達式樹、動態(tài)分發(fā)、Call Site緩存,這可以提高動態(tài)代碼的執(zhí)行效率。在.NET 4.0中我們使用了DLR,不僅僅是IronPython和IronRuby,還有C# 4和VB.NET 1

47、0,它們使用DLR實現(xiàn)動態(tài)分發(fā)功能。因此我們共享了語言的動態(tài)能力實現(xiàn)方式,于是這些語言之間可以輕松地進行交互。同樣我們可以與其他多樣性的技術(shù)進行交互,例如使用JavaScript操作Silverlight的DOM,或是與Ruby、Python代碼溝通,甚至用來控制Office等自動化服務。五、元編程這是Anders Hejlsberg(不用介紹這是誰了吧)在比利時TechDays 2010所做的開場演講。由于最近我在博客上關(guān)于語言的討論比較多,出于應景,也打算將Anders的演講完整地聽寫出來。在上一部分中,Anders談及了他眼中編程語言的另一個發(fā)展趨勢:動態(tài)性。在這一部分中,Anders則

48、討論了動態(tài)語言所擅長的“元編程”,并簡單介紹了他為靜態(tài)類型語言所設(shè)計的一種改進方案:編譯器即服務。如果沒有特別說明,所有的文字都直接翻譯自Anders的演講,并使用我自己的口語習慣表達出來,對于Anders的口誤及反復等情況,必要時在譯文中自然也會進行忽略。為了方便理解,我也會將視頻中關(guān)鍵部分進行截圖,而某些代碼演示則會直接作為文章內(nèi)容發(fā)表。(聽寫開始,接上篇)動態(tài)語言的另一個關(guān)鍵和有趣之處在于“元編程”?!霸幊獭睂嶋H上是“代碼生成”的一種別稱,其實在日常應用中我們也經(jīng)常依賴這種做法。觀察動態(tài)語言適合元編程的原因也是件十分有趣的事情。 在這個藍框中是一段Ruby on Rails代碼(見上圖

49、)。簡單地說,這里定義了一個Order類,繼承了ActiveRecord,也定義了一些關(guān)系,如belongs_to和has_many關(guān)系。Ruby這種動態(tài)語言的關(guān)鍵之處,在于一切事物都是通過執(zhí)行而得到的,包括類型聲明。比如這里的類型申明執(zhí)行了belongs_to和has_many方法的調(diào)用,執(zhí)行belongs_to會截獲一對多或一對一關(guān)系所需要的信息,因此在這里語言是在運行的時候,動態(tài)為自身生成了代碼。 實現(xiàn)這點在動態(tài)語言里自然會更容易一些,因為它們沒有編譯期和執(zhí)行期的區(qū)別。靜態(tài)類型語言在這方面會比較困難。例如在C#或Java里使用ORM時,傳統(tǒng)的做法是讓代碼生成器去觀察數(shù)據(jù)庫,生成一大堆代碼

50、,然后再編譯,有些復雜。不過我時常想著去改善這一點。其中一種做法,是我們正在努力實現(xiàn)的“編譯器即服務”,我現(xiàn)在先對它進行一些簡單的介紹。傳統(tǒng)的編譯器像是一個黑盒,你在一端輸入代碼,而另一端便會生成.NET程序集或是對象代碼等等。而這個黑盒卻很神秘,你目前很難參與或理解它的工作。 你可以想象,一些代碼往往是不包含在源文件中的。如果你想要交互式編程的體驗,例如一個交互式的提示符,那么代碼不是保存在源文件中而是由用戶輸入的。如果您在實現(xiàn)一個DSL,例如Windows Workflow或是Biztalk,則可能用C#或VB實現(xiàn)了一些需要動態(tài)執(zhí)行的規(guī)則,它們也不是保存在源文件中,而可能是放在XML屬性中

51、的。此時你想編譯它們卻做不到,你還是要把它們放入源文件,這就變的復雜了。另一方面,對于編譯器來說,我們不一定需要它生成程序集,有時候需要的是一些樹狀的表現(xiàn)形式。例如一些由用戶反射生成的代碼,便可能不要程序集而是一個解析樹,然后可以對它進行識別和重寫。因此,我們可能越來越需要的是一些API,以此開放編譯器的功能。 例如,你可以給它一小段代碼,讓它返回一段可執(zhí)行的程序,或是一個可以識別或重寫的解析樹。這么做可以讓靜態(tài)類型語言獲得許多有用的功能,例如元編程,以及可操作的完整的對象模型等等。我們正在朝這方面努力,我也會在下午1點的C# 4.0演講中談論更多這方面的內(nèi)容。六、并發(fā)這是Anders Hej

52、lsberg(不用介紹這是誰了吧)在比利時TechDays 2010所做的開場演講。由于最近我在博客上關(guān)于語言的討論比較多,出于應景,也打算將Anders的演講完整地聽寫出來。在上一部分中,Anders談論了“元編程”及他正在努力的“編譯器即服務”功能。在這一部分中,Anders則談論了“并發(fā)”,這也是他眼中編程語言發(fā)展的三種趨勢之一,并演示了.NET 4.0中并行庫的神奇效果。如果沒有特別說明,所有的文字都直接翻譯自Anders的演講,并使用我自己的口語習慣表達出來,對于Anders的口誤及反復等情況,必要時在譯文中自然也會進行忽略。為了方便理解,我也會將視頻中關(guān)鍵部分進行截圖,而某些代碼演

53、示則會直接作為文章內(nèi)容發(fā)表。 (聽寫開始,接上篇)好,最后我想談的內(nèi)容是“并發(fā)”。 聽說過摩爾定律的請舉手幾乎是所有人。那么多少人聽說了摩爾定律已經(jīng)結(jié)束了呢?嗯,還是有很多人。我有好消息,也有壞消息。我認為摩爾定律并沒有停止。摩爾定律說的是:可以在集成電路上低成本地放置晶體管的數(shù)目,約每兩年便會增加一倍。有趣的是,這個定律從60年代持續(xù)到現(xiàn)在,而從一些跡象上來看,這個定律會繼續(xù)保持20到30年。 摩爾定理有個推論,便是說時鐘速度將根據(jù)相同的周期提高,也就是說每隔大約24個月,CPU的速度便會加倍而這點已經(jīng)停止了。再來統(tǒng)計一下,你們之中有誰的機器里有20GHz的CPU?看到了沒?一個人都沒有。但

54、如果你從五年前開始計算的話,現(xiàn)在我們應該已經(jīng)在使用20GHz的CPU了,但事實并非如此。這點在五年前就停止了,而且事實上最大速度還有些下降,因為發(fā)熱量實在太大了,會消耗許多能源,讓電池用的太快。 有些物理方面的基礎(chǔ)因素讓CPU不能運行的太快。然而,另一意義上的摩爾定理出現(xiàn)了。我們還是可以看到容量的增加,因為可以在同一個表盤上放置多個CPU了。目前已經(jīng)有了雙核、四核,Intel的CTO在三年前說,十年后我們可以出現(xiàn)80核的處理器。 到了那個時候,你的任務管理器中就可能是這樣的。似乎有些嚇人,不過這是我們實驗室中真實存在的128核機器。你可以看到,計算能力已經(jīng)完全用上了。這便是個問題,比如你在這臺

55、強大的機器上進行一個實驗,你自然希望看到100%的使用狀況,不過傳統(tǒng)的實驗都是在一個核上執(zhí)行的,所以我們面臨的挑戰(zhàn)是,我們需要換一種寫程序的方式來利用此類機器。 我的一個同事,Herb Sutter,他寫過一篇文章,談到“免費的午餐已經(jīng)結(jié)束了”。沒錯,我們已經(jīng)不能寫一個程序,然后對客戶說:啊,未來的硬件會讓它運行的越來越快,我們不用關(guān)心太多不,已經(jīng)不會這樣了,除非你換種不同的寫法。實話說,這是個挑戰(zhàn),也是個機遇。說它是個挑戰(zhàn),是因為并發(fā)十分困難,至今我們對此還沒有簡單的答案,稍后我會演示一些正有所改善的東西,但這也是一個機遇,在這樣的機器上,你的確可以用完所有的核,這樣便能獲得性能提高,不過做

56、法需要有所不同。 多核革命的一個有趣之處在于,它對于并發(fā)的思維方式會有所改變。傳統(tǒng)的并發(fā)思維是在單個CPU上執(zhí)行多個邏輯任務,使用舊有的分時方式、時間片模型來執(zhí)行多個任務。但是,你想一下便會發(fā)現(xiàn)如今的并發(fā)情況正好相反,現(xiàn)在是要將一個邏輯上的任務放在多個CPU上執(zhí)行。這改變了我們編寫程序的方式,這意味著對于語言或是API來說,我們需要有辦法來分解任務,把它拆分成多個小任務后獨立的執(zhí)行,而傳統(tǒng)的編程語言中并不關(guān)注這點。 使用目前的并發(fā)API來完成工作并不容易,比如使用Thread,ThreadPool,lock,Monitor等等,你無法太好的進展。不過.NET 4.0提供了一些美妙的事物,我們稱

57、之為.NET并行擴展。它是一種現(xiàn)代的并發(fā)模型,將邏輯上的任務并發(fā)與我們實際使用的的物理模型分離開來。以前我們的API都是直接處理線程,也就是(上圖)下方橙色的部分,不過有了.NET并行擴展之后,你可以使用更為邏輯化的編程風格。任務并行庫(Task Parallel Library),并行LINQ(Parallel LINQ)以及協(xié)調(diào)數(shù)據(jù)結(jié)構(gòu)(Coordination Data Structures)讓你可以直接關(guān)注邏輯上的任務,而不必關(guān)心它們是如何運行的,或是使用了多少個線程和CPU等等。 下面我來簡單演示一下它們的使用方式。我?guī)砹艘粋€PLINQ演示,這里是一些代碼,讀取XML文件的內(nèi)容。這有個50M大小的popname.xml文件,保存了美國社會安全數(shù)據(jù)庫里的信息,包含某個洲在某一年的人口統(tǒng)計信息。這個程序會讀取這個XML文件,把它轉(zhuǎn)化成一系列對象,并存放在一個List中。然后對其執(zhí)行一個LINQ語句,查找所有在華盛頓名叫Robert的人,再根據(jù)年份進行排序:Console.WriteLine("Loadi

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論