<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 牛人業(yè)話(huà) > 編程語(yǔ)言的發(fā)展趨勢及未來(lái)方向(2):聲明式編程與DSL

編程語(yǔ)言的發(fā)展趨勢及未來(lái)方向(2):聲明式編程與DSL

作者: 時(shí)間:2017-03-31 來(lái)源:網(wǎng)絡(luò ) 收藏

  這是Anders Hejlsberg(不用介紹這是誰(shuí)了吧)在比利時(shí)TechDays 2010所做的開(kāi)場(chǎng)演講。由于最近我在博客上關(guān)于語(yǔ)言的討論比較多,出于應景,也打算將Anders的演講完整地聽(tīng)寫(xiě)出來(lái)。在上一部分中,Anders指出語(yǔ)言本身在過(guò)去的數十年里并沒(méi)有明顯的發(fā)展,并給出了他眼中發(fā)展趨勢的預測。在現在的第2部分中,Anders將闡述聲明式編程的理念及,并演示C#中一種內部的形式:LINQ。

本文引用地址:http://dyxdggzs.com/article/201703/346085.htm

  如果沒(méi)有特別說(shuō)明,所有的文字都直接翻譯自Anders的演講,并使用我自己的口語(yǔ)習慣表達出來(lái),對于A(yíng)nders的口誤及反復等情況,必要時(shí)在譯文中自然也會(huì )進(jìn)行忽略。為了方便理解,我也會(huì )將視頻中關(guān)鍵部分進(jìn)行截圖,而某些代碼演示則會(huì )直接作為文章內容發(fā)表。

  (聽(tīng)寫(xiě)開(kāi)始,接上篇)

    

 

  這里先從聲明式(Declarative)編程談起。

    

 

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

  有多種方式可以將“What”轉化為更為“聲明式”的編程風(fēng)格,我們只要能夠在代碼中體現出更多“What”,而不是“How”的信息,這樣執行環(huán)境便可以更加聰明地去適應當前的執行要求。例如,它可以決定投入多少CPU進(jìn)行計算,你的當前硬件是什么樣的,等等。

    

 

  我之前提到過(guò),現在有兩種比較重要的成果,一是(Domain Specific Language,領(lǐng)域特定語(yǔ)言),另一個(gè)則是函數式編程。

  其實(shí)DSL不是什么新鮮的玩意兒,我們平時(shí)一直在用類(lèi)似的東西,比如,SQL,CSS,正則表達式,有的可能更加專(zhuān)注于一個(gè)方面,例如Mathematica,LOGO等等。這些語(yǔ)言的目標都是特定的領(lǐng)域,與之相對的則是GPPL(General Purpose Programming Language,通用目的)。

    

 

  對于DSL而言其實(shí)并沒(méi)有一個(gè)明確的定義,在這里我也不打算為它下個(gè)定義,例如UML甚至根本沒(méi)有特定的語(yǔ)法。不過(guò)我這里會(huì )談一些我覺(jué)得比較重要的東西。

    

 

  Martin Fowler提出DSL應該分為外部DSL及內部DSL兩種,我認為這種劃分方式還是比較有意義的。外部DSL是自我包含的語(yǔ)言,它們有自己特定語(yǔ)法、解析器和詞法分析器等等,它往往是一種小型的編程語(yǔ)言,甚至不會(huì )像GPPL那樣需要源文件。與之相對的則是內部DSL。內部DSL其實(shí)更像是種別稱(chēng),它代表一類(lèi)特別API及使用模式。這里我會(huì )給你們看一些示例。

    

 

  這些是我們平時(shí)會(huì )遇到的一些外部DSL,如這張幻燈片上表現的XSLT,SQL或是Unix腳本。外部DSL的特點(diǎn)是,你在構建這種DSL時(shí),其實(shí)扮演的是編程語(yǔ)言設計者的角色,這個(gè)工作并不會(huì )交給普通人去做。外部DSL一般會(huì )直接針對特定的領(lǐng)域設計,而不考慮其他東西。James Gosling曾經(jīng)說(shuō)過(guò)這樣的話(huà),每個(gè)配置文件最終都會(huì )變成一門(mén)編程語(yǔ)言。你一開(kāi)始可能只會(huì )用它表示一點(diǎn)點(diǎn)東西,然后慢慢你便會(huì )想要一些規則,而這些規則則變成了表達式,可能你還會(huì )定義變量,進(jìn)行條件判斷等等。而最終它就變成了一種奇怪的編程語(yǔ)言,這樣的情況屢見(jiàn)不鮮。

  事實(shí)上,現在有一些公司也在關(guān)注DSL的開(kāi)發(fā)。例如以前在微軟工作的Charles Simonyi提出了Intentional Programming的概念,還有一個(gè)叫做JetBrains的公司提供一個(gè)叫做MPS(Meta Programming System)的產(chǎn)品。最近微軟也提出了自己的Oslo項目,而在Eclipse世界里也有個(gè)叫做Xtext的東西,所以其實(shí)在這方面現在也有不少人在嘗試。

  我在觀(guān)察外部DSL時(shí),往往會(huì )關(guān)注它的語(yǔ)法到底提供了多少空間,例如一種XML的方言,利用XML方言的好處在于有不少現成的工具可用,這樣可以更快地定義自己的語(yǔ)法。

    

 

  而內部DSL,正像我之前說(shuō)的那樣,它其實(shí)只是一系列特別的API及使用模式的別稱(chēng)。這里則是一些LINQ查詢(xún)語(yǔ)句,Ruby on Rails以及jQuery代碼。內部DSL的特點(diǎn)是,它其實(shí)只是一系列API,但是你可以“假裝”它們一種DSL。內部DSL往往會(huì )利用一些“流暢化”的技巧,例如像這里的LINQ或jQuery那樣把一些方法通過(guò)“點(diǎn)”連接起來(lái)。有些則利用了元編程的方式,如這里的Ruby on Rails就涉及到了一些元編程。這種DSL可以訪(fǎng)問(wèn)語(yǔ)言中的代碼或變量,以及利用如代碼補全,重構等母語(yǔ)言的所有特性。

    

 

  現在我會(huì )花幾分鐘時(shí)間演示一下我所創(chuàng )建的DSL,也就是LINQ。我相信你們也已經(jīng)用過(guò)不少LINQ了,不過(guò)這里我還是快速的展示一下我所表達的更為“聲明式”的編程方式。

  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 GetProducts() { /* ... */ }

  }

  public partial class _Default : System.Web.UI.Page

  {

  protected void Page_Load(object sender, EventArgs e)

  {

  List products = Product.GetProducts();

  List result = new List();

  foreach (Product p in products)

  {

  if (p.UnitPrice > 20) result.Add(p);

  }

  GridView1.DataSource = result;

  GridView1.DataBind();

  }

  }

  這里有許多Product對象,那么現在我要篩選出所有單價(jià)大于20的那些, 再把他們顯示在一個(gè)GridView中。傳統的做法就是這樣,我先得到所有的Product對象,然后foreach遍歷每個(gè)對象,再判斷每個(gè)對象的單價(jià),最終把數據綁定到GridView里。運行這個(gè)程序……(打開(kāi)頁(yè)面)這就是就能得到結果。

  好,那么現在我要做一些稍微復雜的事情??赡芪也皇且故締蝺r(jià)超過(guò)20的Product對象,而是要查看每個(gè)分類(lèi)中究竟有多少個(gè)單價(jià)超過(guò)20的對象,然后根據數量進(jìn)行排序。如果不用DSL完成這個(gè)工作,那么我可能會(huì )先定義一個(gè)對象來(lái)表示結果:

  class Grouping

  {

  public string CategoryName { get; set; }

  public int ProductCount { get; set; }

  }

  這是個(gè)表示分組的對象,用于保存分類(lèi)的名稱(chēng)和產(chǎn)品數量。然后我們就會(huì )寫(xiě)一些十分丑陋的代碼:

  Dictionary groups = new Dictionary();

  foreach (Product p in products)

  {

  if (p.UnitPrice >= 20)

  {

  if (!groups.ContainsKey(p.CategoryName))

  {

  Grouping r = new Grouping();

  r.CategoryName = p.CategoryName;

  r.ProductCount = 0;

  groups[p.CategoryName] = r;

  }

  groups[p.CategoryName].ProductCount++;

  }

  }

  List result = new List(groups.Values);

  result.Sort(delegate(Grouping x, Grouping y)

  {

  return

  x.ProductCount > y.ProductCount ? -1 :

  x.ProductCount < y.ProductCount ? 1 :

  0;

  });

  我先創(chuàng )建一個(gè)新的字典,用于保存分類(lèi)名稱(chēng)到分組的對應關(guān)系。然后我遍歷每個(gè)Product對象,對于每個(gè)單價(jià)大于20的對象,如果字典中還沒(méi)有保存對應的分組則創(chuàng )建一個(gè),然后將數量加一。然后為了排序,我調用Sort方法,于是我要提供一個(gè)委托作為排序方法,然后blablablabla……執行之后……(打開(kāi)頁(yè)面)我自然可以得到想要的結果。

  但是,首先這些代碼寫(xiě)起來(lái)需要花費一些時(shí)間,很顯然。然后仔細觀(guān)察,你會(huì )發(fā)現這寫(xiě)代碼幾乎都是在表示“How”,而“What”基本已經(jīng)丟失了。假設我離開(kāi)了,現在新來(lái)了一個(gè)程序員要維護這段代碼,他會(huì )需要一點(diǎn)時(shí)間才能完整理解這段代碼,因為他無(wú)法直接看清代碼的目標。

  不過(guò)如果這里我們使用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……先調用Where……blablabla……再GroupBy等等。由于我們這里可以使用DSL來(lái)表示高階的術(shù)語(yǔ),用以體現我們想做的事情。于是這段代碼則更加關(guān)注于“What”而不是“How”。我這里不會(huì )明確地指示我想要過(guò)濾的方式,我也不會(huì )明確地說(shuō)我要建立字典和分類(lèi),這樣基礎結構就可以聰明地,或者說(shuō)更加聰明地去確定具體的執行方式。你可能比較容易想到我們可以并行地執行這段代碼,因為我沒(méi)有顯式地指定做事方式,我只是表示出我的意圖。

  我們打開(kāi)頁(yè)面……(打開(kāi)頁(yè)面)很顯然我們得到了相同的結果。

  這里比較有趣的是,內部DSL是如何設計進(jìn)C#語(yǔ)法中的,為此我們?yōu)镃# 3.0添加了一系列的特性,例如Lambda表達式,擴展方法,類(lèi)型推斷等等。這些特性統一起來(lái)之后,我們就可以設計出更為豐富的API,組合之后便成為一種內部DSL,就像這里的LINQ查詢(xún)語(yǔ)言。

  除了使用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() };

  編譯器會(huì )簡(jiǎn)單地將這種形式轉化為前一種形式。不過(guò),這里我認為有意思的地方在于,你完全可以創(chuàng )建一門(mén)和領(lǐng)域編程語(yǔ)言完全無(wú)關(guān)的語(yǔ)法,然后等這種語(yǔ)法和API變得流行且豐富起來(lái)之后,再來(lái)創(chuàng )一種新的表現形式,就如這里的LINQ查詢(xún)語(yǔ)法。我頗為中意這種語(yǔ)言設計的交流方式。

  OK,現在我們回到下面的內容。

  (未完待續)



關(guān)鍵詞: 編程語(yǔ)言 DSL

評論


相關(guān)推薦

技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>