當前位置:
首頁 > 科技 > 改變開發者編碼思維的六種編程範式

改變開發者編碼思維的六種編程範式

編者按:


現在公眾號有置頂功能了,大家把微信更新到最新版本,點開「大數據實驗室」公眾號。點「置頂公眾號」鍵,就可以置頂了,這樣。不管我們什麼時候更新,您都能容易找到。


譯者註:本文介紹了六種編程範式,提到了不少小眾語言,作者希望藉此讓大家更多的了解一些非主流的編程範式,進而改變對編程的看法。以下為譯文:


時不時地,我會發現一些編程語言所做的一些與眾不同的事情,也因此改變了我對編碼的看法。在本文,我將把這些發現分享給大家。


這不是「函數式編程將改變世界」的那種陳詞濫調的博客文章,這篇文章列舉的內容更加深奧。我敢打賭大部分讀者都沒有聽說過下面這些語言和範式,所以我希望大家能像我當初一樣,帶著興趣去學習這些新概念,並從中找到樂趣。

註:對於下面講到的大多數語言,我擁有的經驗其實很少:我只是覺得他們背後的思想很贊,但對於它們沒有任何相關的專業知識,所以有任何更正和錯誤請指出。此外,如果你發現任何沒有包括在這裡的新的範式和想法,請分享它們!


更新:這篇文章上了r/programming和HN首頁,感謝反饋,我將會進行更正。


默認支持並發(Concurrent by default)

改變開發者編碼思維的六種編程範式



示例語言:ANI, Plaid


讓我們先從改變思維開始:有一些編程語言是默認支持並發的。也就是說,每一行代碼都是並行執行的!


例如,假設你寫了三行代碼,A,B和C:


A;


B;

C;


在大多數編程語言中,A將首先執行,接著B執行,最後C執行。在像ANI這樣的編程語言中,A, B和 C將同時執行。


在ANI中代碼行之間的控制流或者順序只是代碼行之間顯式依賴的副作用。例如,如果B對A中定義的變數有引用,那麼A和C將同時執行,而B將在A完成後執行。


來看一個ANI的例子。正如教程中所描述的,ANI 程序由用於操作流和數據流的「管道」和「鎖存器」組成。這種非同一般的語法很難解析,ANI這門語言似乎已經死了,但概念還是相當有趣的。


下面是ANI中的「Hello World」示例:


"Hello, World!" ->std.out


在ANI語法中,我們將「Hello World!」對象(一個字元串)發送到std.out流。如果我們發送另外一個字元串到std.out會怎樣?


"Hello, World!" ->std.out


"Goodbye, World!" ->std.out


這兩行代碼並發執行,所以它們可能以任意順序在控制台輸出。現在,看看當我們在一行中引入一個變數並在之後引用會發生什麼:

s = [string];


"Hello, World!" ->s;


s ->std.out;


第一行聲明一個叫做s的「鎖存器」(鎖存器有點像變數),其中包含一個字元串;第二行發送文本「Hello World!」發送到s;第三行「解鎖」s並將內容發送到std.out。因此,你可以看到ANI的隱式程序排序:因為每一行運行都依賴於上一行,因此,這段代碼將按照它編寫的順序執行。


Plaid語言也聲稱默認情況下支持並發,但使用的是本篇論文中所描述的一種許可權模型來構建控制流。 Plaid還探討了其它有趣的概念,如面向類型狀態的編程,在那裡狀態轉換成為了語言中的重要因素:你定義的對象不再是類,而是一系列可以由編譯器檢查的狀態和轉換。看起來十分有趣,正如Rich Hickey在演講「Are we there yet」中所討論的,將時間作為語言結構的首要因素。


Multicore正處在上升期,並發性仍然比大多數語言更難。ANI 和 Plaid 對於這個可能產生驚人的性能提升的問題提供了一個新的思路;不過問題是「默認支持並行」是否讓並發更容易或難以管理。


更新:上面的描述講解了ANI和Plaid的基本本質,但我可互換地使用術語「並發」和「並行」,即使它們有不同的含義。如果想了解更多信息請閱讀「並發不是並行」這篇文章。


依賴類型 (Dependent types)

改變開發者編碼思維的六種編程範式


示例語言:Idris, Agda, Coq


你可能習慣於像C和JAVA等語言的類型系統,編譯器可以檢查一個變數是整數,列表,或者字元串。但是如果你的編譯器可以檢查一個變數是「正整數」,「長度為2的列表」,還是「一個迴文字元串」會怎樣呢?


這就是支持依賴類型語言背後的思想:你可以在編譯時指定檢查變數值得類型。Scala的Shapeless庫添加了對Scala依賴類型的部分實驗性質支持,並提供了觀察一些例子的簡單方法。


下面是如何聲明一個Vector的代,其中使用了shapeless庫,包含值1、2、3:


val l1 = 1 :#: 2 :#: 3 :#: VNil


這裡創建了一個變數l1,它的類型簽名不僅指定它是一個包含Ints的Vector,還指定了它的長度是3。編譯器可以使用這個信息來捕獲錯誤。讓我們使用Vector中的vAdd方法來執行兩個Vector間的成對相加(pairwise addition):


val l1 = 1 :#: 2 :#: 3 :#: VNil


val l2 = 1 :#: 2 :#: 3 :#: VNil


// Result: l3 = 2 :#: 4 :#: 6 :#: VNil


上面的例子運行正常,因為類型系統知道兩個Vector的長度都是3.。然而,如果我們嘗試兩個長度不同的Vectors,我們會在編譯時得到一個錯誤。

val l1 = 1 :#: 2 :#: 3 :#: VNil


val l2 = 1 :#: 2 :#: VNil


// Result: a *compile* error because you can t pairwise add vectors


// of different lengths!


Shapeless是一個了不起的庫,但在我看來,它仍然有點粗糙,只支持依賴類型的一個子集,並導致生成相當詳細的代碼和類型簽名。另一方面,Irdris,使類型成為編程語言的首要成員,所以,依賴類型系統似乎更強大和更乾淨。為了比較,可以看看「Scala VS Idris:從屬依賴類型的,在現在和未來」演講。


形式化驗證方法已經存在很長一段時間了,但往往過於繁瑣,不適用於通用編程。依賴類型的語言,如Idris,甚至在未來的Scala中,可能會提供更輕量級和更實用的替代方案,這仍然可以顯著的提高類型系統捕捉錯誤的能力。當然,由於終止問題的固有限制,沒有哪個依賴類型系統可以捕捉到全部錯誤,但如果做得好,依賴類型可能是靜態類型系統下一個大的飛躍。


拼接語言(Concatenative languages)

改變開發者編碼思維的六種編程範式



示例語言:Forth, cat ,joy

想像過,在沒有變數和函數應用的情況下,編寫程序是什麼樣子的嗎?沒有?我也沒試過。但顯然有人做了,他們提出了拼接編程。這個概念背後的思想是語言中的都是把數據壓入堆棧或者彈出堆棧的函數;程序幾乎完全通過功能組合來構建(基於堆棧的編程語言)。


這聽起來相當抽象,所以讓我們來看cat語言中一個簡單的例子:


2 3 +


在這裡,我們將兩個數字推倒堆棧上,然後調用+函數,它將兩個數字從堆棧中彈出,並將它們相加的結果添加到堆棧:代碼的輸出是5。下面是一個更有趣一點的例子:


def foo {


[ 0 ]


[ 42 ]


if


}


20

foo


讓我們逐行解讀上面的代碼:


首先,我們聲明一個函數foo。注意,在CAT中函數不指定輸入參數:所有參數都是從堆棧中隱式讀取的。


foo調用


接下來,我們將0和42推到堆棧:我們把它們放在括弧中以確保它們推到未被評估堆棧上。這是因為這是因為它們將被用作「then」和「else」分支(分別)用於調用下一行的 if 函數。


if函數在堆棧中彈出3個選項:布爾條件、「then」和「else」分支。根據布爾條件的值,它將會把「then」或「else」分支的結果推回到堆棧。


最後,我們將20推到堆棧並調用函數foo。


當上面所說流程都完成後,我們將最終得到數字42.


更詳細的介紹,請參看「 The Joy of Concatenative Languages 」 :http://www.codecommit.com/blog/cat/the-joy-of-concatenative-languages-part-1


這種編程風格有一些有趣的屬性:

程序可以通過無數種方式分割和連接以創建新的程序;


極簡的語法(甚至比 LISP 還小)產生了非常簡潔的程序;


強大的元編程支持


我發現拼接編程是一個令人大開眼界的思想實驗,但我還未實踐過。似乎你必須記住或想像堆棧的當前狀態,而不能夠從代碼中的變數名讀取它,這會使代碼很難理解。


聲明式編程(Declarative programming)

改變開發者編碼思維的六種編程範式



示例語言:Prolog, SQL


聲明式編程已經存在了許多年,但大多數程序員仍然不知道它是怎樣的概念。簡單來說:在大多數主流語言中,開發者是在描述如何解決一個特定的問題;在聲明式語言中,你只需要描述你想要的結果,而語言本身確定如何到達那裡。


例如,如果你使用C語言從頭開始寫一個排序演算法,你可能會為合并排序寫一個說明,一步一步的描述如何遞歸地將數據集分割成兩部分並將其合并到一起:這裡是一個例子。如果使用聲明式語言如Prolog來進行數字排序,可直接描述你想要的輸出:「我想要相同的值列表,但每個索引i中的每個項目都應小於或等於索引為i+ 1的項」。將前面的C語言解決方案和下面的Prolog代碼進行對比:

sort_list(Input, Output) :-


permutation(Input, Output),


check_order(Output).


check_order([]).


check_order([Head]).


check_order([First, Second | Tail]) :-


First =< Second,


check_order([Second | Tail]).


如果你使用過SQL,那麼你已經使用了聲明式編程,可能自己沒有意識到這一點:當你發出一個像 select X from Y where Z 這樣的查詢,你就是在描述你想要返回的數據集;資料庫引擎的工作實際上是如何執行查詢。你可以在大多數資料庫中使用 explain 命令來查看執行計劃並弄清楚在引擎下發生了什麼。


聲明式語言之美在於它們允許你在更高層次的抽象下工作:你的工作就是描述你想要的輸出規格。例如,在Prolog語言中一個簡單的數獨求解器的代碼只需要列出每行,每列,和一個解決的數獨難題的對角線應該看起來的樣子:


sudoku(Puzzle, Solution) :-


Solution = Puzzle,


Puzzle = [S11, S12, S13, S14,


S21, S22, S23, S24,


S31, S32, S33, S34,


S41, S42, S43, S44],


fd_domain(Solution, 1, 4),


Row1 = [S11, S12, S13, S14],


Row2 = [S21, S22, S23, S24],


Row3 = [S31, S32, S33, S34],


Row4 = [S41, S42, S43, S44],


Col1 = [S11, S21, S31, S41],


Col2 = [S12, S22, S32, S42],


Col3 = [S13, S23, S33, S43],


Col4 = [S14, S24, S34, S44],


Square1 = [S11, S12, S21, S22],


Square2 = [S13, S14, S23, S24],


Square3 = [S31, S32, S41, S42],


Square4 = [S33, S34, S43, S44],


valid([Row1, Row2, Row3, Row4,


Col1, Col2, Col3, Col4,


Square1, Square2, Square3, Square4]).


valid([]).


valid([Head | Tail]) :- fd_all_different(Head), valid(Tail).


下面是如何運行上面的數獨求解器:


| ?- sudoku([_, _, 2, 3,


_, _, _, _,


_, _, _, _,


3, 4, _, _],


Solution).


S = [4,1,2,3,2,3,4,1,1,2,3,4,3,4,1,2]


不幸的是,聲明式編程語言的性能開銷比較大。上面的單純排序演算法的複雜度接近O(n!);數獨求解器使用暴力搜索;而且大多數開發人員不得不提供資料庫提示和額外索引,以避免執行SQL查詢時的昂貴和低效的計劃。


符號式編程(Symbolic programming)

改變開發者編碼思維的六種編程範式



示例語言:Aurora


Aurora語言是符號式編程的一個例子:使用符號編程語言編寫的「代碼」不僅包括純文本,還包括圖像、數學方程、圖、圖表等。這允許你以數據的原生格式來操作和描述大量的數據,而不是完全用文本來描述它。Aurora是完全互動式的,它會立即顯示每行代碼的結果,像steroids中的REPL。


Aurora語言是由Chris Granger創造的,他還創建了Light Table IDE。克里斯在他的文章《為了更好地編程》中描述了創建Aurora的動機,目標是使編程更直觀,直接,減少偶然的複雜性。欲了解更多信息,請參見Bret Victor的演講:Inventing on Principle, Media for Thinking the Unthinkable, and Learnable Programming。


更新:「符號編程」可能不是適用於Aurora。更多信息參見維基百科上的「符號編程」的維基主頁。


基於知識的編程(Knowledge-based programming)

改變開發者編碼思維的六種編程範式



示例語言:Wolfram語言


與上面提到的Aurora語言很像,Wolfram語言也是基於符號編程的。然而,符號層僅僅是提供一種與Wolfram語言核心一致的介面,Wolfram語言是基於知識的編程語言:內置了大量的庫、演算法和數據。這使得可以輕鬆地從圖形化的Facebook連接,到處理圖像,查找天氣,處理自然語言查詢,繪製地圖上的方向,解決數學方程等等。


我猜想Wolfram語言擁有現存語言中最大的「標準庫」和任何現有語言的數據集。我被「互聯網連接(Internet connectivity)是代碼編寫的固有部分」這個想法所打動:它就像一個通過谷歌搜索來實現自動完成功能的集成開發IDE。這將是非常有趣的,看看符號編程模型是否像Wolfram聲稱的那樣靈活,可以真正利用所有這些數據。


來源:CSDN大數據


作者:Yevgeniy Brikman


翻譯:陳學明


量化投資實戰課程——全明星六大海龜豪華陣容傾囊相授


吳帆? AIMA中國區總經理


聶軍? 凱思博香港總經理


毛煜春? 安誠數盈董事長


張弘?深圳盈富總經理


林健武?量化總監


章贇? 量化總監


2017年6月16日—18日 上海


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 大數據實驗室 的精彩文章:

TAG:大數據實驗室 |

您可能感興趣

圖形化編程與代碼對照解讀是培養編程思維的好辦法
改變你的思維
數字出版編輯思維轉型:從編輯走向產品經理
有限式思維模式與無限式思維模式
思維模式改變了失敗的意義
思維模式決定了你的發展程度
繪本+桌游,培養孩子編程思維和數理邏輯的訓練!
精緻≠娘炮丨從思維改變男性的護膚理念
程廷芳:形象思維和邏輯思維
測命的思維程序
善於改變自己的思維和習慣
抑鬱治療|抑鬱者這七種獨特的思維和認知方式,需要改變
成為優秀的技術管理者:先從改變思維做起
改變世界的發明:解碼人類思維轉化為語言
2種數據科學編程中的思維模式,了解一下(附代碼)
抑鬱症患者如何改變思維模式《學會提問:批判性思維指南》筆記
推特帖子反映一天中的思維模式變化
抑鬱症從改變自己的負性思維開始
建立診改制度要創新思維
新的經驗塑造新的思維模式