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

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

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

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

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

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

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

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

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

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

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

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

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

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

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

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

15、。我相信你們也已經用過不少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對象,那么現在我要篩選出所有單價大于20的那些, 再把他們顯示在一個GridView中。傳統的做法就是這樣,我先得到所有的Product對象,然后foreach遍歷每個對象,再判斷每個對象的單價,最終把數據綁定到GridView里。運行這個程序(打開頁面)這就是就能得到結果。 好,那么現在我要做一些稍微復雜的事情。可能我不是要展示單價超過20的Product對象,而是要查看每個分類中究竟有多少個單價超過20的對象,然后根據數量進行排序。如果不用DSL完成這個工作,那么我可能會先定義一個對象來表示結果:class Grouping public string Cate

18、goryName get; set; public int ProductCount get; set; 這是個表示分組的對象,用于保存分類的名稱和產品數量。然后我們就會寫一些十分丑陋的代碼: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;);我先創建一個新的字典,用于保存分類名稱到分組的對應關系。然后我遍歷每個Product對象,對于每個單價大于20的對象,如果字典中還沒有保存對應的分組則創建一個,然后將數量加一。然后為了排序,我調用Sort方法,于是我要提供一個委托作為排序方法,然后blablablabla執行之后(打開頁面)我自然可以得到想要的結果。 但是,首先這些代碼寫起來需要花費一些時間,很顯然。然后仔細觀察,你會發現這寫代碼幾乎都是在表示“How”,而“What”基本已經丟失了。假設我離開了,現在新來了一個程序員要維護這段代碼,他會需要一點時間才能完整理解這

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先調用Whereblablabla再GroupBy等等。由于我們這里可以使用DSL來表示高階的

22、術語,用以體現我們想做的事情。于是這段代碼則更加關注于“What”而不是“How”。我這里不會明確地指示我想要過濾的方式,我也不會明確地說我要建立字典和分類,這樣基礎結構就可以聰明地,或者說更加聰明地去確定具體的執行方式。你可能比較容易想到我們可以并行地執行這段代碼,因為我沒有顯式地指定做事方式,我只是表示出我的意圖。 我們打開頁面(打開頁面)很顯然我們得到了相同的結果。 這里比較有趣的是,內部DSL是如何設計進C#語法中的,為此我們為C# 3.0添加了一系列的特性,例如Lambda表達式,擴展方法,類型推斷等等。這些特性統一起來之后,我們就可以設計出更為豐富的API,組合之后便成為一種內部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() ;編譯器會簡單地將這種形式轉化為前一種形式。不過,這里我認為有意思的地方在于,你完全可以創建一門和領域編程語言完全無關的語法,然后等這種語法和API變得流行且豐富

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

溫馨提示

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

評論

0/150

提交評論