http://geek.csdn.net/news/detail/195967?utm_source=tuicool&utm_medium=referral
原文:Six programming paradigms that will change how you think about coding
作者:Yevgeniy Brikman
翻譯:陳學(xué)明
譯者注:本文介紹了六種編程范式,提到了不少小眾語(yǔ)言,作者希望借此讓大家更多的了解一些非主流的編程范式瞭亮,進(jìn)而改變對(duì)編程的看法瘫筐。以下為譯文:
時(shí)不時(shí)地西潘,我會(huì)發(fā)現(xiàn)一些編程語(yǔ)言所做的一些與眾不同的事情程奠,也因此改變了我對(duì)編碼的看法芋膘。在本文蒸绩,我將把這些發(fā)現(xiàn)分享給大家衙四。 這不是“函數(shù)式編程將改變世界”的那種陳詞濫調(diào)的博客文章,這篇文章列舉的內(nèi)容更加深?yuàn)W侵贵。我敢打賭大部分讀者都沒(méi)有聽(tīng)說(shuō)過(guò)下面這些語(yǔ)言和范式届搁,所以我希望大家能像我當(dāng)初一樣,帶著興趣去學(xué)習(xí)這些新概念窍育,并從中找到樂(lè)趣卡睦。 注:對(duì)于下面講到的大多數(shù)語(yǔ)言,我擁有的經(jīng)驗(yàn)其實(shí)很少:我只是覺(jué)得他們背后的思想很贊漱抓,但對(duì)于它們沒(méi)有任何相關(guān)的專(zhuān)業(yè)知識(shí)表锻,所以有任何更正和錯(cuò)誤請(qǐng)指出。此外乞娄,如果你發(fā)現(xiàn)任何沒(méi)有包括在這里的新的范式和想法瞬逊,請(qǐng)分享它們! 更新:這篇文章上了r/programming和HN首頁(yè)仪或,感謝反饋确镊,我將會(huì)進(jìn)行更正。
默認(rèn)支持并發(fā)(Concurrent by default)
示例語(yǔ)言:ANI, Plaid 讓我們先從改變思維開(kāi)始:有一些編程語(yǔ)言是默認(rèn)支持并發(fā)的范删。也就是說(shuō)蕾域,每一行代碼都是并行執(zhí)行的! 例如到旦,假設(shè)你寫(xiě)了三行代碼旨巷,A,B和C:
A; B; C;
在大多數(shù)編程語(yǔ)言中添忘,A將首先執(zhí)行采呐,接著B(niǎo)執(zhí)行,最后C執(zhí)行搁骑。在像ANI這樣的編程語(yǔ)言中斧吐,A又固, B和 C將同時(shí)執(zhí)行。 在ANI中代碼行之間的控制流或者順序只是代碼行之間顯式依賴(lài)的副作用会通。例如口予,如果B對(duì)A中定義的變量有引用,那么A和C將同時(shí)執(zhí)行涕侈,而B(niǎo)將在A完成后執(zhí)行。 來(lái)看一個(gè)ANI的例子煤辨。正如教程中所描述的裳涛,ANI 程序由用于操作流和數(shù)據(jù)流的“管道”和“鎖存器”組成。這種非同一般的語(yǔ)法很難解析众辨,ANI這門(mén)語(yǔ)言似乎已經(jīng)死了端三,但概念還是相當(dāng)有趣的。 下面是ANI中的“Hello World”示例:
"Hello, World!" ->std.out
在ANI語(yǔ)法中鹃彻,我們將“Hello World郊闯!”對(duì)象(一個(gè)字符串)發(fā)送到std.out流。如果我們發(fā)送另外一個(gè)字符串到std.out會(huì)怎樣蛛株?
"Hello, World!" ->std.out "Goodbye, World!" ->std.out
這兩行代碼并發(fā)執(zhí)行团赁,所以它們可能以任意順序在控制臺(tái)輸出。現(xiàn)在谨履,看看當(dāng)我們?cè)谝恍兄幸胍粋€(gè)變量并在之后引用會(huì)發(fā)生什么:
s = [string]; "Hello, World!" ->s; \s ->std.out;
第一行聲明一個(gè)叫做s的“鎖存器”(鎖存器有點(diǎn)像變量)欢摄,其中包含一個(gè)字符串;第二行發(fā)送文本“Hello World笋粟!”發(fā)送到s怀挠;第三行“解鎖”s并將內(nèi)容發(fā)送到std.out。因此害捕,你可以看到ANI的隱式程序排序:因?yàn)槊恳恍羞\(yùn)行都依賴(lài)于上一行绿淋,因此,這段代碼將按照它編寫(xiě)的順序執(zhí)行尝盼。 Plaid語(yǔ)言也聲稱(chēng)默認(rèn)情況下支持并發(fā)吞滞,但使用的是本篇論文中所描述的一種權(quán)限模型來(lái)構(gòu)建控制流。 Plaid還探討了其它有趣的概念东涡,如面向類(lèi)型狀態(tài)的編程冯吓,在那里狀態(tài)轉(zhuǎn)換成為了語(yǔ)言中的重要因素:你定義的對(duì)象不再是類(lèi),而是一系列可以由編譯器檢查的狀態(tài)和轉(zhuǎn)換疮跑∽楹兀看起來(lái)十分有趣,正如Rich Hickey在演講“Are we there yet”中所討論的祖娘,將時(shí)間作為語(yǔ)言結(jié)構(gòu)的首要因素失尖。 Multicore正處在上升期啊奄,并發(fā)性仍然比大多數(shù)語(yǔ)言更難。ANI 和 Plaid 對(duì)于這個(gè)可能產(chǎn)生驚人的性能提升的問(wèn)題提供了一個(gè)新的思路掀潮;不過(guò)問(wèn)題是“默認(rèn)支持并行”是否讓并發(fā)更容易或難以管理菇夸。 更新:上面的描述講解了ANI和Plaid的基本本質(zhì),但我可互換地使用術(shù)語(yǔ)“并發(fā)”和“并行”仪吧,即使它們有不同的含義庄新。如果想了解更多信息請(qǐng)閱讀“并發(fā)不是并行”這篇文章。
依賴(lài)類(lèi)型 (Dependent types)
示例語(yǔ)言:Idris, Agda, Coq 你可能習(xí)慣于像C和JAVA等語(yǔ)言的類(lèi)型系統(tǒng)薯鼠,編譯器可以檢查一個(gè)變量是整數(shù)择诈,列表,或者字符串出皇。但是如果你的編譯器可以檢查一個(gè)變量是“正整數(shù)”羞芍,“長(zhǎng)度為2的列表”,還是“一個(gè)回文字符串”會(huì)怎樣呢郊艘? 這就是支持依賴(lài)類(lèi)型語(yǔ)言背后的思想:你可以在編譯時(shí)指定檢查變量值得類(lèi)型荷科。Scala的Shapeless庫(kù)添加了對(duì)Scala依賴(lài)類(lèi)型的部分實(shí)驗(yàn)性質(zhì)支持,并提供了觀察一些例子的簡(jiǎn)單方法纱注。 下面是如何聲明一個(gè)Vector的代畏浆,其中使用了shapeless庫(kù),包含值1奈附、2全度、3:
val l1 = 1 :#: 2 :#: 3 :#: VNil
這里創(chuàng)建了一個(gè)變量l1,它的類(lèi)型簽名不僅指定它是一個(gè)包含Ints的Vector斥滤,還指定了它的長(zhǎng)度是3将鸵。編譯器可以使用這個(gè)信息來(lái)捕獲錯(cuò)誤。讓我們使用Vector中的vAdd方法來(lái)執(zhí)行兩個(gè)Vector間的成對(duì)相加(pairwise addition):
val l1 = 1 :#: 2 :#: 3 :#: VNil val l2 = 1 :#: 2 :#: 3 :#: VNilval l3 = l1 vAdd l2// Result: l3 = 2 :#: 4 :#: 6 :#: VNil
上面的例子運(yùn)行正常佑颇,因?yàn)轭?lèi)型系統(tǒng)知道兩個(gè)Vector的長(zhǎng)度都是3.顶掉。然而,如果我們嘗試兩個(gè)長(zhǎng)度不同的Vectors挑胸,我們會(huì)在編譯時(shí)得到一個(gè)錯(cuò)誤痒筒。
val l1 = 1 :#: 2 :#: 3 :#: VNilval l2 = 1 :#: 2 :#: VNilval l3 = l1 vAdd l2// Result: a compile error because you can't pairwise add vectors // of different lengths!
Shapeless是一個(gè)了不起的庫(kù),但在我看來(lái)茬贵,它仍然有點(diǎn)粗糙簿透,只支持依賴(lài)類(lèi)型的一個(gè)子集,并導(dǎo)致生成相當(dāng)詳細(xì)的代碼和類(lèi)型簽名解藻。另一方面老充,Irdris,使類(lèi)型成為編程語(yǔ)言的首要成員螟左,所以啡浊,依賴(lài)類(lèi)型系統(tǒng)似乎更強(qiáng)大和更干凈觅够。為了比較,可以看看“Scala VS Idris:從屬依賴(lài)類(lèi)型的巷嚣,在現(xiàn)在和未來(lái)”演講喘先。 形式化驗(yàn)證方法已經(jīng)存在很長(zhǎng)一段時(shí)間了吏奸,但往往過(guò)于繁瑣味榛,不適用于通用編程帮非。依賴(lài)類(lèi)型的語(yǔ)言沐序,如Idris,甚至在未來(lái)的Scala中伪很,可能會(huì)提供更輕量級(jí)和更實(shí)用的替代方案募舟,這仍然可以顯著的提高類(lèi)型系統(tǒng)捕捉錯(cuò)誤的能力徘溢。當(dāng)然景东,由于終止問(wèn)題的固有限制,沒(méi)有哪個(gè)依賴(lài)類(lèi)型系統(tǒng)可以捕捉到全部錯(cuò)誤奔誓,但如果做得好斤吐,依賴(lài)類(lèi)型可能是靜態(tài)類(lèi)型系統(tǒng)下一個(gè)大的飛躍。
拼接語(yǔ)言(Concatenative languages)
示例語(yǔ)言:Forth厨喂, cat 和措,joy 想象過(guò),在沒(méi)有變量和函數(shù)應(yīng)用的情況下蜕煌,編寫(xiě)程序是什么樣子的嗎派阱?沒(méi)有?我也沒(méi)試過(guò)斜纪。但顯然有人做了贫母,他們提出了拼接編程。這個(gè)概念背后的思想是語(yǔ)言中的都是把數(shù)據(jù)壓入堆椇懈眨或者彈出堆棧的函數(shù)腺劣;程序幾乎完全通過(guò)功能組合來(lái)構(gòu)建(基于堆棧的編程語(yǔ)言)。 這聽(tīng)起來(lái)相當(dāng)抽象因块,所以讓我們來(lái)看cat語(yǔ)言中一個(gè)簡(jiǎn)單的例子:
2 3 +
在這里橘原,我們將兩個(gè)數(shù)字推倒堆棧上,然后調(diào)用+函數(shù)涡上,它將兩個(gè)數(shù)字從堆棧中彈出趾断,并將它們相加的結(jié)果添加到堆棧:代碼的輸出是5。下面是一個(gè)更有趣一點(diǎn)的例子:
def foo { 10 < [ 0 ] [ 42 ] if}20foo
讓我們逐行解讀上面的代碼:
首先吩愧,我們聲明一個(gè)函數(shù)foo芋酌。注意,在CAT中函數(shù)不指定輸入?yún)?shù):所有參數(shù)都是從堆棧中隱式讀取的耻警。
foo調(diào)用<函數(shù)隔嫡,它從堆棧上彈出堆棧的第一個(gè)選項(xiàng)甸怕,將其與10進(jìn)行比較,并將true或false返回到堆棧腮恩。
接下來(lái)梢杭,我們將0和42推到堆棧:我們把它們放在括號(hào)中以確保它們推到未被評(píng)估堆棧上。這是因?yàn)檫@是因?yàn)樗鼈儗⒈挥米鳌皌hen”和“else”分支(分別)用于調(diào)用下一行的 if 函數(shù)秸滴。
if函數(shù)在堆棧中彈出3個(gè)選項(xiàng):布爾條件武契、“then”和“else”分支。根據(jù)布爾條件的值荡含,它將會(huì)把“then”或“else”分支的結(jié)果推回到堆棧咒唆。
最后,我們將20推到堆棧并調(diào)用函數(shù)foo释液。
當(dāng)上面所說(shuō)流程都完成后全释,我們將最終得到數(shù)字42.
更詳細(xì)的介紹,請(qǐng)參看“ The Joy of Concatenative Languages ” 這種編程風(fēng)格有一些有趣的屬性:
程序可以通過(guò)無(wú)數(shù)種方式分割和連接以創(chuàng)建新的程序误债;
極簡(jiǎn)的語(yǔ)法(甚至比 LISP 還薪)產(chǎn)生了非常簡(jiǎn)潔的程序;
強(qiáng)大的元編程支持
我發(fā)現(xiàn)拼接編程是一個(gè)令人大開(kāi)眼界的思想實(shí)驗(yàn)寝蹈,但我還未實(shí)踐過(guò)李命。似乎你必須記住或想象堆棧的當(dāng)前狀態(tài),而不能夠從代碼中的變量名讀取它箫老,這會(huì)使代碼很難理解封字。
聲明式編程(Declarative programming)
示例語(yǔ)言:Prolog, SQL 聲明式編程已經(jīng)存在了許多年,但大多數(shù)程序員仍然不知道它是怎樣的概念耍鬓。簡(jiǎn)單來(lái)說(shuō):在大多數(shù)主流語(yǔ)言中阔籽,開(kāi)發(fā)者是在描述如何解決一個(gè)特定的問(wèn)題;在聲明式語(yǔ)言中界斜,你只需要描述你想要的結(jié)果仿耽,而語(yǔ)言本身確定如何到達(dá)那里。 例如各薇,如果你使用C語(yǔ)言從頭開(kāi)始寫(xiě)一個(gè)排序算法项贺,你可能會(huì)為合并排序?qū)懸粋€(gè)說(shuō)明,一步一步的描述如何遞歸地將數(shù)據(jù)集分割成兩部分并將其合并到一起:這里是一個(gè)例子峭判。如果使用聲明式語(yǔ)言如Prolog來(lái)進(jìn)行數(shù)字排序开缎,可直接描述你想要的輸出:“我想要相同的值列表,但每個(gè)索引i中的每個(gè)項(xiàng)目都應(yīng)小于或等于索引為i+ 1的項(xiàng)”林螃。將前面的C語(yǔ)言解決方案和下面的Prolog代碼進(jìn)行對(duì)比:
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]).
如果你使用過(guò)SQL奕删,那么你已經(jīng)使用了聲明式編程,可能自己沒(méi)有意識(shí)到這一點(diǎn):當(dāng)你發(fā)出一個(gè)像 select X from Y where Z 這樣的查詢(xún)疗认,你就是在描述你想要返回的數(shù)據(jù)集完残;數(shù)據(jù)庫(kù)引擎的工作實(shí)際上是如何執(zhí)行查詢(xún)伏钠。你可以在大多數(shù)數(shù)據(jù)庫(kù)中使用 explain 命令來(lái)查看執(zhí)行計(jì)劃并弄清楚在引擎下發(fā)生了什么。 聲明式語(yǔ)言之美在于它們?cè)试S你在更高層次的抽象下工作:你的工作就是描述你想要的輸出規(guī)格谨设。例如熟掂,在Prolog語(yǔ)言中一個(gè)簡(jiǎn)單的數(shù)獨(dú)求解器的代碼只需要列出每行,每列扎拣,和一個(gè)解決的數(shù)獨(dú)難題的對(duì)角線應(yīng)該看起來(lái)的樣子: 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).
下面是如何運(yùn)行上面的數(shù)獨(dú)求解器:
| ?- sudoku([_, _, 2, 3, _, _, _, _, _, _, _, _, 3, 4, _, _], Solution).S = [4,1,2,3,2,3,4,1,1,2,3,4,3,4,1,2]
不幸的是赴肚,聲明式編程語(yǔ)言的性能開(kāi)銷(xiāo)比較大。上面的單純排序算法的復(fù)雜度接近O(n6丁)誉券;數(shù)獨(dú)求解器使用暴力搜索;而且大多數(shù)開(kāi)發(fā)人員不得不提供數(shù)據(jù)庫(kù)提示和額外索引刊愚,以避免執(zhí)行SQL查詢(xún)時(shí)的昂貴和低效的計(jì)劃踊跟。
符號(hào)式編程(Symbolic programming)
示例語(yǔ)言:Aurora Aurora語(yǔ)言是符號(hào)式編程的一個(gè)例子:使用符號(hào)編程語(yǔ)言編寫(xiě)的“代碼”不僅包括純文本,還包括圖像鸥诽、數(shù)學(xué)方程琴锭、圖、圖表等衙传。這允許你以數(shù)據(jù)的原生格式來(lái)操作和描述大量的數(shù)據(jù),而不是完全用文本來(lái)描述它厕九。Aurora是完全交互式的蓖捶,它會(huì)立即顯示每行代碼的結(jié)果,像steroids中的REPL扁远。 Aurora語(yǔ)言是由Chris Granger創(chuàng)造的俊鱼,他還創(chuàng)建了Light Table IDE〕┞颍克里斯在他的文章《為了更好地編程》中描述了創(chuàng)建Aurora的動(dòng)機(jī)并闲,目標(biāo)是使編程更直觀,直接谷羞,減少偶然的復(fù)雜性帝火。欲了解更多信息,請(qǐng)參見(jiàn)Bret Victor的演講:Inventing on Principle, Media for Thinking the Unthinkable, and Learnable Programming湃缎。 更新:“符號(hào)編程”可能不是適用于Aurora犀填。更多信息參見(jiàn)維基百科上的“符號(hào)編程”的維基主頁(yè)。
基于知識(shí)的編程(Knowledge-based programming)
示例語(yǔ)言:Wolfram語(yǔ)言 與上面提到的Aurora語(yǔ)言很像嗓违,Wolfram語(yǔ)言也是基于符號(hào)編程的九巡。然而,符號(hào)層僅僅是提供一種與Wolfram語(yǔ)言核心一致的接口蹂季,Wolfram語(yǔ)言是基于知識(shí)的編程語(yǔ)言:內(nèi)置了大量的庫(kù)冕广、算法和數(shù)據(jù)疏日。這使得可以輕松地從圖形化的Facebook連接,到處理圖像撒汉,查找天氣沟优,處理自然語(yǔ)言查詢(xún),繪制地圖上的方向神凑,解決數(shù)學(xué)方程等等净神。 我猜想Wolfram語(yǔ)言擁有現(xiàn)存語(yǔ)言中最大的“標(biāo)準(zhǔn)庫(kù)”和任何現(xiàn)有語(yǔ)言的數(shù)據(jù)集。我被“互聯(lián)網(wǎng)連接(Internet connectivity)是代碼編寫(xiě)的固有部分”這個(gè)想法所打動(dòng):它就像一個(gè)通過(guò)谷歌搜索來(lái)實(shí)現(xiàn)自動(dòng)完成功能的集成開(kāi)發(fā)IDE溉委。這將是非常有趣的鹃唯,看看符號(hào)編程模型是否像Wolfram聲稱(chēng)的那樣靈活,可以真正利用所有這些數(shù)據(jù)瓣喊。 更新:雖然Wolfram聲稱(chēng)Wolfram語(yǔ)言支持“符號(hào)式編程”和“知識(shí)編程”坡慌,但這些術(shù)語(yǔ)的定義是有所不同的。更多信息請(qǐng)參閱 Knowledge level 和 Symbolic Programming 的維基主頁(yè)藻三。