Javascript函數式程式設計語言_javascript技巧

來源:互聯網
上載者:User

函數式程式設計語言

函數式程式設計語言是那些方便於使用函數式編程範式的語言。簡單來說,如果具備函數式編程所需的特徵, 它就可以被稱為函數式語言。在多數情況下,編程的風格實際上決定了一個程式是否是函數式的。

是什麼讓一個語言具有函數式特徵?

函數式編程無法用C語言來實現。函數式編程也無法用Java來實現(不包括那些通過大量變通手段實現的近似函數式編程)。 這些語言不包含支援函數式編程的結構。他們是純物件導向的、嚴格非函數式的語言。

同時,純函數語言也無法使用物件導向編程,比如Scheme、Haskell以及Lisp。

然而有些語言兩種模式都支援。Python是個著名的例子,不過還有別的:Ruby,Julia,以及我們最感興趣的Javascript。 這些語言是如何支援這兩種差別如此之大的設計模式呢?它們包含兩種編程範式所需要的特徵。 然而對於Javascript來說,函數式的特徵似乎是被隱藏了。

但實際上,函數式語言所需要的比上述要多一些。到底函數式語言有什麼特徵呢?

特點 命令式 函數式
編程風格 一步一步地執行,並且要管理狀態的變化 描述問題和和所需的資料變化以解決問題
狀態變化 很重要 不存在
執行順序 很重要 不太重要
主要的控制流程 迴圈、條件、函數調用 函數調用和遞迴
主要的操作單元 結構體和類對象 函數作為一等公民的對象和資料集

函數式語言的文法必須要顧及到特定的設計模式,比如類型推斷系統和匿名函數。大體上,這個語言必須實現lambda演算。 並且解譯器的求值策略必須是非嚴格、按需調用的(也叫做順延強制),它允許不變資料結構和非嚴格、惰性求值。

譯註:這一段用了一些函數式編程的專業詞彙。lambda演算是一套函數推演的形式化系統(聽起來很暈), 它的先決條件是內建函式和匿名函數。非嚴格求值和惰性求值差不多一個意思,就是並非嚴格地按照運算規則把所有元素先計算一遍, 而是根據最終的需求只計算有用的那一部分,比如我們要取有一百個元素的數組的前三項, 那惰性求值實際只會計算出一個具有三個元素是數組,而不會先去計算那個一百個元素的數組。 

優點

當你最終掌握了函數式編程它將給你巨大的啟迪。這樣的經驗會讓你後面的程式員生涯更上一個台階, 無論你是否真的會成為一個全職的函數式程式員。

不過我們現在不是在討論如何去學習冥想;我們正在探討如何去學習一個非常有用的工具,它將會讓你成為一個更好的程式員。

總的來說,什麼是使用函數式編程真正實際的優點呢?

更加簡潔的代碼

函數式編程更簡潔、更簡單、更小。它簡化了調試、測試和維護。

例如,我們需要這樣一個函數,它能將二維數組轉化為一維數組。如果只用命令式的技術,我們會寫成這樣:

function merge2dArrayIntoOne(arrays) {  var count = arrays.length;  var merged = new Array(count);  var c = 0;  for (var i = 0; i < count; ++i) {   for (var j = 0, jlen = arrays[i].length; j < jlen; ++j) {    merged[c++] = arrays[i][j];   }  }  return merged}

現在使用函數式技術,可以寫成這樣:

merge2dArrayIntoOne2 = (arrays) -> arrays.reduce (memo, item) ->  memo.concat item , []
var merge2dArrayIntoOne2 = function(arrays) { return arrays.reduce( function(p,n){  return p.concat(n); }, []);};
譯註:原著中代碼有誤,調用reduce函數時少了第二個參數空數組,這裡已經補上。

這兩個函數具有同樣的輸入並返回相同的輸出,但是函數式的例子更簡潔。

模組化

函數式編程強制把大型問題拆分成解決同樣問題的更小的情形,這就意味著代碼會更加模組化。 模組化的程式具有更清晰的描述,更易調試,維護起來也更簡單。測試也會變得更加容易, 這是由於每一個模組的代碼都可以單獨檢測正確性。

複用性

由於其模組化的特性,函數式編程會有許多通用的輔助函數。你將會發現這裡面的許多函數可以在大量不同的應用裡重用。

在後面的章節裡,許多最通用的函數將會被覆蓋到。然而,作為一個函數式程式員,你將會不可避免地編寫自己的函數庫, 這些函數會被一次又一次地使用。例如一個用於在行間尋找設定檔的函數,如果設計好了也可以用於尋找Hash表。

減少耦合

耦合是程式裡模組間的大量依賴。由於函數式編程遵循編寫一等公民的、高階的純函數, 這使得它們對全域變數沒有副作用而彼此完全獨立,耦合極大程度上的減小了。 當然,函數會不可避免地相互依賴,但是改變一個函數不會影響其他的,只要輸入和輸出的一對一映射保持正確。

數學正確性

最後一點更理論一些。由於根植於lambda演算,函數式編程可以在數學上證明正確性。 這對於一些研究者來說是一個巨大的優點,他們需要用程式來證明增長率、時間複雜度以及數學正確性。

我們來看看斐波那契數列。儘管它很少用於概念性證明以外的問題,但是用它來解釋這個概念非常好。 對一個斐波那契數列求值標準的辦法是建立一個遞迴函式,像這樣:

fibonnaci(n) = fibonnaci(n-2) + fibonnaci(n–1) 

還需要加上一個一般情形:

return 1 when n < 2

這使得遞迴可以終止,並且讓遞迴調用棧裡的每一步從這裡開始累加。

下面列出詳細步驟

var fibonacci = function(n) { if (n < 2) {  return 1;  }else {  return fibonacci(n - 2) + fibonacci(n - 1); } }console.log( fibonacci(8) );// Output: 34

然而,在一個懶執行函數庫的輔助下,可以產生一個無窮大的序列,它是通過數學方程來定義整個序列的成員的。 只有那些我們最終需要的成員最後才會被計算出來。

var fibonacci2 = Lazy.generate(function() { var x = 1, y = 1; return function() {  var prev = x;  x = y;  y += prev;  return prev; }; }());console.log(fibonacci2.length());// Output: undefinedconsole.log(fibonacci2.take(12).toArray());// Output: [1, 1, 2, 3, 5,8, 13, 21, 34, 55, 89, 144]var fibonacci3 = Lazy.generate(function() { var x = 1, y = 1; return function() {  var prev = x;  x = y;  y += prev;  return prev; }; }());console.log(fibonacci3.take(9).reverse().first(1).toArray());//Output: [34]

第二個例子明顯更有數學的味道。它依賴Lazy.js函數庫。還有一些其它這樣的庫,比如Sloth.js、wu.js, 這些將在第三章裡面講到。

我插幾句:後面這個懶執行的例子放這似乎僅僅是來秀一下函數式編程在數學正確性上的表現。 更讓人奇怪的是作者還要把具有相同內建函式的懶載入寫兩遍,完全沒意義啊…… 我覺得各位看官知道這是個懶執就行了,不必深究。

非函數式世界中的函數式編程

函數式和非函數式編程能混合在一起嗎?儘管這是第七章的主題,但是在我們進一步學習之前, 還是要弄明白一些東西。

這本書並沒要想要教你如何嚴格地用純函數編程來實現整個應用。這樣的應用在學術界之外不太適合。 相反,這本書是要教你如何在必要的命令式代碼之上使用純函數的設計策略。

例如,你需要在一段文本中找出頭四個只含有字母的單詞,稚嫩一些的寫法會是這樣:

var words = [], count = 0;text = myString.split(' ');for (i=0; count < 4, i < text.length; i++) { if (!text[i].match(/[0-9]/)) {  words = words.concat(text[i]);  count++; } }console.log(words);

函數式編程會寫成這樣:

var words = [];var words = myString.split(' ').filter(function(x){ return (! x.match(/[1-9]+/));}).slice(0,4);console.log(words);

如果有一個函數式編程的工具庫,代碼可以進一步被簡化:

複製代碼 代碼如下:

var words = toSequence(myString).match(/[a-zA-Z]+/).first(4);

判斷一個函數是否能被寫成更加函數式的方式是尋找迴圈和臨時變數,比如前面例子裡面的“words”和”count”變數。 我們通常可以用高階函數來替換迴圈和臨時變數,本章後面的部分將對其繼續探索。

Javascript是函數式程式設計語言嗎?

現在還有最後一個問題我們需要問問自己,Javascript是函數式語言還是非函數式語言?

Javascript可以說是世界上最流行卻最沒有被理解的函數式程式設計語言。Javascript是一個披著C外衣的函數式程式設計語言。 它的文法無疑和C比較像,這意味著它使用C語言的塊式文法和中綴語序。並且它是現存語言中名字起得最差勁的。 你不用去想象就可以看出來有多少人會因Javascript和Java的關係而迷惑,就好像它的名字暗示了它會是什麼樣的東西! 但實際上它和Java的共同點非常少。不過還真有一些要把Javascript強制弄成物件導向語言的主意, 比如Dojo、ease.js這些庫曾做了大量工作試圖抽象Javascript以使其適合物件導向編程。 Javascript來自於90年代那個滿世界都嚷嚷著物件導向的時代,我們被告知Javascript是一個物件導向語言是因為我們希望它是這樣, 但實際上它不是。

它的真實身份可以追溯到它的原型:Scheme和Lisp,兩個經典的函數式程式設計語言。Javascript一直都是一個函數式程式設計語言。 它的函數是頭等公民,並且可以嵌套,它具有閉包和複合函數,它允許珂理化和monad。所有這些都是函數式編程的關鍵。 這裡另外還有一些Javascript是函數式語言的原因:

• Javascript的詞法包括了傳遞函數為參數的能力,具有類型推斷系統,支援匿名函數、高階函數、閉包等等。 這些特點對構成函數式編程的結構和行為至關重要。

• Javascript不是一個純物件導向語言,它的多數物件導向設計模式都是通過拷貝Prototype對象來完成的, 這是一個弱物件導向編程的模型。歐洲電腦製造商協會指令碼(ECMAScript)——Javascript的正式形式和標準實現 ——在4.2.1版本的規範裡有如下陳述:
“Javascript不具有像C++、Smalltalk、Java那樣的真正的類,但是支援建立對象的構造器。 一般來說,在基於類的物件導向語言裡,狀態由執行個體承載,方法由類承載,繼承只是針對結構和行為。 在EMACScript裡,狀態和方法由對象來承載,結構、行為和狀態都會被繼承。”

• Javascript是一個解釋型語言。Javascript的解譯器(有時被稱為“引擎”)非常類似於Scheme的解譯器。 它們都是動態,都有易於組合和傳輸的靈活的資料類型,都把代碼求值為運算式塊,處理函數的方式也類似。

也就是說,Javascript的確不是一個純函數式語言。它缺乏惰性求值和內建的不可變資料。 這是由於大多數解譯器是按名調用,而不是按需調用。Javascript由於其尾調用的處理方式也不太善於處理遞迴。 不過所有的這些問題都可以通過一些小的注意事項來緩和。需要無窮序列和惰性求值的非嚴格求值可以通過一個叫Lazy.js的庫來實現。 不可變數只需要簡單的通過編程技巧就可以實現,不過它不是通過依賴語言層面來限制而是需要程式員自律。 尾遞迴消除可以通過一個叫Trampolining的方法實現。這些問題將在第六章講解。

關於Javascript是函數式語言還是物件導向語言還是兩者皆是還是兩者皆非的爭論一直都很多,而且這些爭論還要繼續下去。

最後,函數式編程是通過巧妙的變化、組合、使用函數而實現編寫簡潔代碼的方式。而且Javascript為實現這些提供了很好的途徑。 如果你真要挖掘出Javascript全部的潛能,你必須學會如何將它作為一個函數式語言來使用。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.