小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

跨越邊界: 用 Haskell 研究函數(shù)性編程

 魚非魚 2007-03-28
將此頁作為電子郵件發(fā)送                                                    

                                               

 

結(jié)構(gòu)化編程和面向?qū)ο缶幊潭几镄铝藰I(yè)務(wù)應(yīng)用程序構(gòu)建的方式。但是還存在其他編程模型,有些夢想家還認為這些范式比面向?qū)ο缶幊痰纳a(chǎn)力更高。這篇文章探索 Haskell 研究函數(shù)性編程的基礎(chǔ)。學(xué)習(xí)函數(shù)性編程可以重塑對于 Java? 編程的思考方式。

過去 50 年中,企業(yè)所使用的語言 —— COBOL、C、C++ 和 Java 語言,都是命令式 語言;它們讓您告訴您的程序如何 去完成其任務(wù)。函數(shù)性 編程語言讓您告訴程序去做什么。這篇文章通過介紹 Haskell 來研究函數(shù)性編程。(如果您閱讀過我的跨越邊界 系列中 關(guān)于使用另外一種函數(shù)性語言 Erlang 進行并發(fā)性編程的文章,可能已經(jīng)有了一定的基礎(chǔ)。)

在我研究超越 Java(請參閱 參考資料)時,我采訪的三位專家提到了 Haskell,他們認為我應(yīng)該探索一下這種語言。當(dāng)時,我并不認為市場已經(jīng)為函數(shù)性編程做好了準(zhǔn)備,因為這一范式對于大多數(shù)程序員來說都太陌生了。我現(xiàn)在仍然不認為我們已經(jīng)做好了準(zhǔn)備。但是我開始欣賞函數(shù)性語言帶來的生產(chǎn)力和強大力量。我只是剛剛接觸 Haskell,但這種語言已經(jīng)影響了我使用 Java 和 Ruby 語言解決問題的方式。

命令式語言及其不足

關(guān)于本系列

跨越邊界 系列中,作者 Bruce Tate 推動了這樣一個概念:今天的 Java 程序員可以通過學(xué)習(xí)其他方式和語言受益。Java 技術(shù)曾經(jīng)是編程陣營中所有開發(fā)項目的最佳選擇,但這種情況已經(jīng)發(fā)生了變化。其他框架正在塑造 Java 框架的構(gòu)建方式,從其他語言中學(xué)到的概念有助于 Java 編程。所編寫的 Python(或 Ruby、Smalltalk 或 ... 請自由填空)代碼可以改變您編寫 Java 代碼的方式。

本系列介紹了完全不同于 Java 開發(fā),但又直接適用于 Java 開發(fā)的編程概念和技術(shù)。在某些情況下,需要集成之后才能利用這些技術(shù)。在其他情況下,則可以直接應(yīng)用這些概念。單獨的工具不如其他語言和框架影響 Java 社區(qū)的開發(fā)人員、框架、甚至基本手段的這個概念重要。

命令式編程由一系列帶有明確順序的語句構(gòu)成,它們的算法和編程構(gòu)造嚴(yán)重依賴于應(yīng)用程序的狀態(tài)。很難想像沒有這些特征的編程語言,因為命令式語言 “告訴我如何做” 的方式是如此深刻地確立在我們的日常編程哲學(xué)中。命令式編程明確塑造了您的思維方式。但通過學(xué)習(xí)替代的函數(shù)性編程工具,可以擴展您思考問題的方式。請考慮以下這些命令式結(jié)構(gòu):

  • 賦值。命令式編程的用戶對賦值的依賴要超過其他編程技術(shù)。函數(shù)性編程最多允許為每個變量賦值一次。改變值的賦值被叫做 破壞性賦值,是不允許的。例如,多數(shù)函數(shù)性語言不允許 x = x + 1。

  • 迭代。命令式編程利用迭代來處理許多不同類型的數(shù)據(jù)結(jié)構(gòu)。許多命令式控制依賴破壞性賦值進行迭代。

  • 副作用。在命令式語言中,輸入相同而可能返回不同值的方法,都有副作用。而對應(yīng)用程序的狀態(tài)變量有影響的方法也有副作用。函數(shù)性編程沒有副作用。

如果以前沒用過函數(shù)性語言,那么就很難想像如何編寫沒有破壞性賦值和副作用的應(yīng)用程序。但是這些基本特征給命令性語言帶來了一些更嚴(yán)重的問題:

  • 正確性問題。有副作用的方法可能返回一個值,但同時也修改了應(yīng)用程序的狀態(tài)。副作用把驗證程序正確的數(shù)學(xué)問題復(fù)雜化。但是復(fù)雜不僅僅是數(shù)學(xué)問題:副作用還使得測試、理解、分析和記錄程序的行為更加困難。

  • 基于順序的 bug。許多優(yōu)化依賴于對關(guān)鍵編程指令重新排序來提高性能。當(dāng)依賴特定順序的程序被重新排序時,錯誤就發(fā)生了。

  • 并發(fā)性問題??勺儬顟B(tài)消失時,差不多所有的并發(fā)性問題也隨之消失。Brian Goetz 在 Java Concurrency in Practice(請參閱 參考資料)中,用 “這是愚蠢的可變狀態(tài)“ 規(guī)則結(jié)束了第一章。

不管信還是不信,不必強行規(guī)定操作的順序或承擔(dān)副作用,也可以有效地編程。





回頁首


函數(shù)性編程簡介

在數(shù)學(xué)中,函數(shù)把每個輸入映射到一個特定的輸出。函數(shù)性編程范式使用數(shù)學(xué)函數(shù)表達程序。函數(shù)性語言并不執(zhí)行命令,而是通過表示、計算數(shù)學(xué)函數(shù)來解決問題。函數(shù)性語言通常有以下兩個特征:

  • 不可變的數(shù)據(jù)。純粹的函數(shù)性語言不依賴保存或檢索狀態(tài)的操作,因此不允許破壞性賦值。
  • 無副作用。用相同的輸入調(diào)用函數(shù),總是返回相同的值。

對大多數(shù)函數(shù)性語言來說,這有點過于簡化,但是只是過了一點點。函數(shù)叫做單體(monad),被用來以數(shù)學(xué)方式表達狀態(tài)的變化,而 Haskell 這樣的函數(shù)性語言則用單體來處理輸入/輸出并管理狀態(tài)中的變化。

看到函數(shù)性編程的一些局限性后,您可能認為這種編程范式是一種倒退,但請繼續(xù)往下閱讀。函數(shù)性語言不是軟弱無力的。實際上,語言專家們通常相信函數(shù)性語言操作的抽象級別要比面向?qū)ο笳Z言高。它們提供了命令式語言通常不提供的一些工具。在這篇文章中,將看到一些工具的工作效果。





回頁首


使用 Haskell

有兩個 Haskell 實現(xiàn)值得注意:Hugs 和 Glasgow Haskell Compiler(GHC)(請參閱 參考資料)。還有許多其他 Haskell 編譯器和解釋器,包括 Hugs 和 GHC 的分支,但是它們是主要的兩個。如果剛接觸 Haskell,那么 Hugs 解釋器是個不錯的選擇,因為它安裝和理解起來都比較容易。Hugs 有兩方面嚴(yán)重的限制:它缺乏編譯器,不能使用獨立函數(shù);必須從文件裝入全部函數(shù)。更嚴(yán)謹(jǐn)?shù)某绦騿T會采用 GHC。它的解釋器略慢一些,但是它有編譯器模式,還允許獨立函數(shù)。在這篇文章中,我使用 Hugs,所以如果您想根據(jù)本文編寫代碼,也應(yīng)當(dāng)使用它,因為這兩套軟件使用的術(shù)語略有不同。

用 Hugs 編碼

請下載適合您操作系統(tǒng)的 Hugs (請參閱 參考資料)并啟動它。我把我信任的 Macbook Pro 放在一邊,而用我的 Windows 機器,這是為了得到一個可以快速安裝的環(huán)境。WinHugs 這個 Hugs 實現(xiàn)在 Windows 平臺上有一個簡單的一按即可的安裝程序。

將看到一個帶有 Hugs 提示符的解釋器窗口。啟動即可。輸入一些數(shù)字和數(shù)學(xué)表達式,如清單 1 所示。將看到 Hugs 返回數(shù)學(xué)表達式的結(jié)果。這正是在函數(shù)性語言中期待的行為。


清單 1. 在 Hugs 解釋器中計算
Haskell 98 mode: Restart with command line option -98 to enable extensions
                        Type :? for help
                        Hugs>5
                        5
                        Hugs>5+4
                        9
                        

Haskell 擁有強大的類型模型。語言是強類型的,這意味著您只能在類型的某個實例上完成允許的操作。(例如,如果想把數(shù)字添加到字符串上,Hugs 會報錯。)Haskell 是靜態(tài)類型化的,所以一旦給變量分配了值,那么變量就會一直維持相同的類型。Haskell 會做一些類型推斷,這意味著它會根據(jù)程序中的語義線索來推斷元素的類型,所以您會看到我在使用 有些函數(shù)時沒有聲明相關(guān)的類型。如果使用類型模糊不清或者在函數(shù)中使用不支持的類型,Haskell 會報錯。Haskell 還有子類型,而且完全是多態(tài)的;這些特性超出了本文的范圍,但如果您對此感興趣,它們也值得研究。

既然已經(jīng)看到了一些原語類型,例如整型,現(xiàn)在可以繼續(xù)了解一些更為復(fù)雜的類型了。通常,一個語言中可用的數(shù)據(jù)結(jié)構(gòu)定義了語言的使用方式。C 使用 struct、Java 使用 class。Haskell 不使用這兩種數(shù)據(jù)結(jié)構(gòu)。

Haskell 中最突出的三種數(shù)據(jù)結(jié)構(gòu)是:tuple、列表(list)用戶定義的類型。我將著重介紹前兩種。tuple 要包含于括號 ( ) 之中,它有固定長度。tuple 包含固定類型的原語元素,甚至可以包含其他 tuple 或列表。相比之下,列表的長度可變,由同類元素構(gòu)成。用 [ ] 包含列表。您可使用 Hugs 來體會 tuple 和列表,如清單 2 所示:


清單 2. 表示簡單的 tuple 和列表
Hugs> [1,2]
                        [1,2]
                        Hugs> [1,"a"]
                        ERROR - Cannot infer instance
                        *** Instance   : Num [Char]
                        *** Expression : [1,"a"]
                        Hugs> (1,2,3)
                        (1,2,3)
                        Hugs> (1,"a")
                        (1,"a")
                        Hugs> [(1,2),(3,4)]
                        [(1,2),(3,4)]
                        Hugs> [(1,2),(3,4),(5,6,7)]
                        ERROR - Type error in list
                        *** Expression     : [(1,2),(3,4),(5,6,7)]
                        *** Term           : (5,6,7)
                        *** Type           : (c,d,e)
                        *** Does not match : (a,b)
                        

在清單 2 中,可以看到 tuple 中的每個元素可以是不同類型的,但列表中的元素必須是相同類型的。而且,如果您使用一個 tuple 列表,那么每個 tuple 的長度必須相同,每個 tuple 中的第 n 個元素必須與列表中所有其他 tuple 中的第 n 個元素匹配。

如您所料,Haskell 有許多在列表上操作的函數(shù)。最簡單的就是 headtail。 head 返回列表的第一個元素,tail 返回其他的元素。清單 3 顯示了一些簡單的列表函數(shù):


清單 3. 簡單的 Haskell 列表函數(shù)
Hugs> [1,2,3,4,5]
                        [1,2,3,4,5]
                        Hugs> length [1,2,3,4,5]
                        5
                        Hugs> head [1,2,3,4,5]
                        1
                        Hugs> tail [1,2,3,4,5]
                        [2,3,4,5]
                        

在清單 3 中,可以看到 head 返回了一個元素,tail 返回了一列元素。稍后還會看到(在 編寫函數(shù) 中)這些函數(shù)如何構(gòu)成了 Haskell 中眾多遞歸函數(shù)的基礎(chǔ)。

在構(gòu)建列表時,使用 : 操作符,叫做構(gòu)造(cons) 操作符(用來構(gòu)造)。構(gòu)建列表時,只是把元素傳遞給另一個列表??梢园言S多構(gòu)建操作串在一起。

字符串只是字符列表的語法代稱而已,像 [1,2,3] 這樣的列表則是 1:2:3:[] 的語法代稱。這個特性使得字符串操作更容易實現(xiàn)。清單 4 顯示了構(gòu)造操作符的的工作方式以及如何用一個字符序列構(gòu)建一個字符串:


清單 4. 用構(gòu)造操作符構(gòu)建列表
Hugs> 6:[]
                        [6]
                        Hugs> 6:[7]
                        [6,7]
                        Hugs> 6:7:[]
                        [6,7]
                        Hugs> ‘d‘:‘o‘:‘g‘:‘ ‘:‘h‘:‘a(chǎn)‘:‘s‘:‘ ‘:‘f‘:‘l‘:‘e‘:‘a(chǎn)‘:‘s‘:[]
                        "dog has fleas"
                        

在 Haskell 中,您會發(fā)現(xiàn)在字符串和列表中這種語法代稱非常普遍。 但是只要記?。阂磺卸际呛瘮?shù)。

把函數(shù)當(dāng)成數(shù)據(jù)

Haskell 允許把函數(shù)當(dāng)成數(shù)據(jù)。這項重要的功能讓眾多的 Haskell 函數(shù)可以接受函數(shù)作為參數(shù)。這個策略讓您能夠用不同的方式把函數(shù)應(yīng)用到函數(shù)的每個元素。清單 5 顯示了一系列函數(shù),它們把函數(shù)應(yīng)用到列表的每個元素:


清單 5. 把函數(shù)作為參數(shù)傳遞
Hugs> :l Char
                        Char> map toUpper "Hello"
                        "HELLO"
                        Char> filter isUpper "To Be or Not to Be"
                        "TBNB"
                        Char> foldr (max) 0 [1,2,3,2,1]
                        3
                        Char> foldr (+) 0 [1,2,3,2,1]
                        9
                        

:l 命令裝入模塊。然后可以調(diào)用 Char 模塊中的函數(shù)。(其他版本的 Haskell 支持 Char.toUpper,但是 Hugs 不支持,所以要先裝入模塊,然后再利用模塊中的函數(shù)。)第一個語句在列表的每個元素上調(diào)用函數(shù)(本例中為 toUpper,用于把字符轉(zhuǎn)成大寫)。因為 Haskell 的字符串就是字符列表,所以得到的是 "HELLO" 字符串。filter 函數(shù)為列表的每個元素調(diào)用一個測試函數(shù),測試函數(shù)返回布爾值,當(dāng)測試函數(shù)返回 True 時,還包含列表的元素。

fold 函數(shù)要略微復(fù)雜一些。它有兩種形式:foldlfoldr。fold 函數(shù)接受一個函數(shù)、一個元素和一個列表??梢赃@樣來看待 fold 函數(shù):

  1. 從列表的左側(cè)(foldl)右側(cè)(foldr)開始放置元素。
  2. 把操作符放在列表的所有元素之間。
  3. 從列表的一側(cè)到另一側(cè)應(yīng)用操作符,對于 foldl 是從左側(cè)開始,對于 foldr 是從右側(cè)開始。

例如,foldr (+) 0 [1,2,3] 可歸納為 1 + (2 + (3 + 0)) 也就是 6。有些時候,順序會有影響,這就是為什么 Haskell 既提供了 foldr(右聯(lián),從右至左構(gòu)建)又提供了 foldl (左聯(lián),從左至右構(gòu)建)的原因。在處理列表時,fold 函數(shù)提供了累積任何二進制計算結(jié)果的好方法。

把函數(shù)組合起來

在 Haskell 程序中,可以用許多不同的方式組合函數(shù)。用復(fù)雜的方式組合函數(shù),是函數(shù)性語言生產(chǎn)力的關(guān)鍵,這是因為這可不斷提高抽象的層次。

用復(fù)雜的方式組合函數(shù),是函數(shù)性語言生產(chǎn)力的關(guān)鍵,這是因為這可不斷提高抽象的層次。

例如,假設(shè)有一個 Java 應(yīng)用程序,它計算句子中大寫字母的數(shù)量。需要對列表進行迭代,每遇到一個大寫字母,就要將一個本地變量加 1。在 Haskell 中,只需用 length (filter (isUpper) "To Be or Not to Be") 取得過濾列表的長度即可。Haskell 程序員就是這樣避免了使用破壞性更新。每個函數(shù)都在內(nèi)部保存中間結(jié)果,程序員不必考慮這類細節(jié)。





回頁首


編寫函數(shù)

如果您使用的是 Hugs,那么需要在獨立的源文件中編寫函數(shù)。WinHugs 可以很好的集成我的編輯器,所以不會造成太大的負擔(dān)。應(yīng)當(dāng)把函數(shù)放在模塊中,然后用 :l 命令裝入模塊。在清單 5 中已經(jīng)看到,我裝入了系統(tǒng)模塊 Char。模塊名稱以及包含模塊的文件,要用大寫字母開始.函數(shù)名用小寫字母開始。Haskell 程序文件的擴展名是 .hs。

可以注意到:Haskell 程序頻繁地用遞歸來解決問題。Java 開發(fā)人員由于性能的原因和堆棧深度的限制,通常會避免遞歸。在處理遞歸時,Haskell 有兩大優(yōu)勢:Haskell 優(yōu)化了尾部遞歸,Haskell 是惰性的

尾部遞歸優(yōu)化意味著當(dāng)遞歸發(fā)生在函數(shù)末尾時,編譯器或解釋器可以把遞歸表示成迭代。尾部遞歸優(yōu)化沒有堆棧開銷,所以這個策略降低了把遞歸處理成簡單迭代的成本。為了理解 “惰性” 的含義,請把以下函數(shù)輸入名為 Generate.hs 的文件:

generate = 1:generate
                        

這個函數(shù)是 1 的無窮列表。更精確地說,generate 通過構(gòu)造操作符,把 1 添加到 generate —— 最初是個空列表。如果裝入并執(zhí)行這個函數(shù),就會進入無窮遞歸循環(huán),因為沒有什么能夠停止遞歸。但奇怪的是,可以在應(yīng)用程序中使用 generate 的結(jié)果,如清單 6 所示:


清單 6. Generate.hs 中的惰性遞歸
Hugs> :l Generate
                        Main> head generate
                        1
                        Main> head (tail generate)
                        1
                        

雖然 generate 代表 1 的無窮列表,但 Haskell 只計算列表中自己需要的部分。在清單 6 中,第一個命令裝入函數(shù),第二個得到頭(或第一個)元素,第三個命令得到末尾的頭(或第二個)元素。Haskell 只計算列表的頭兩個元素。剩下的被延遲,只在需要的時候計算。這種惰性處理的風(fēng)格使得函數(shù)性語言的效率出人意料,而且支持其他編程語言中得不到的叫做無限流 的強大抽象(請參閱 參考資料)。

在大多數(shù)教程中可以發(fā)現(xiàn)的大多數(shù)經(jīng)典 Haskell 函數(shù)都是遞歸的數(shù)學(xué)函數(shù),例如 Fibonacci 函數(shù)和階乘。Fibonacci 序列的定義是:由 1 和 1 開始的序列前兩個數(shù)字的和。所以,F(xiàn)ibonacci 序列的前五個數(shù)字是 1、1、(1+1) = 2、(1+2) = 3 和 (2+3) = 5。Haskell 實現(xiàn)的這個序列與它的數(shù)學(xué)定義非常相似,如清單 7 所示:


清單 7. fib.hs 中的 Fibonacci 序列
fib 1 = 1
                        fib 2 = 1
                        fib x = fib(x-1) + fib(x-2)
                        

可以把清單 7 中的代碼輸入到名為 Fib.hs 的文件中,用 :l fib 裝入它,并輸入 fib(4) 來運行它,生成序列的第四個數(shù)字。請注意,我不需要聲明保存中間結(jié)果的變量。在這個示例中,可以看到更高級別抽象的示例。如果您的問題集恰好適合 Haskell,那么更高級別的抽象就適合您。如果不適合,這種更高級別的抽象將使您陷入麻煩。

可以用與 Fibonacci 序列基本相同的方式對待階乘。x 的階乘就是從 1 到 x 的所有數(shù)字的乘積。在 Haskell 中,可以定義一個計算階乘的函數(shù),如清單 8 所示:


清單 8. 計算階乘
factorial 1 = 1
                        factorial x = x * factorial(x-1)
                        

對列表的遍歷也采用同樣的工作方式。處理列表時,要處理第一個節(jié)點,然后處理列表剩下的部分(剩下的也是列表)。清單 9 顯示了計算列表中所有元素之和的一個遞歸函數(shù):


清單 9. 計算數(shù)字列表的總和
sum [] = 0
                        sum (h:t) = h + sum(t)
                        

如果您以前沒見過這種模式,那么可能需要一點時間來習(xí)慣它。第一行說明空列表的和是 0。第二行代表的概念在幾乎所有函數(shù)性語言中都很普遍。(h:t) 把列表表示成 tuple,列表的頭(包含第一個元素)在 h 內(nèi),列表剩余的元素包含在 t 中。由于尾部遞歸優(yōu)化,這種方式是對列表進行迭代的一種非常簡單的方式。思維過程與代碼的匹配非常好:做第一件事,剩下的事后面再做;什么都不剩時,就完成了。

用同樣的方式也可以遍歷非常復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。只要多付出一點功夫,就能表達 B 樹 —— B 樹的每個節(jié)點都容納一個值和兩個分支。用 Haskell 表示的簡單 B 樹形式如下:

data Tree a = Null | Node (Tree a) a (Tree a)
                        

在這個示例中,data 是構(gòu)建抽象類型的手段。a 是類型。它告訴 Haskell:樹可以可以什么都不包含,也可以是由樹構(gòu)成的,樹后面跟著值,后面又跟著另一個樹。樹中的每個節(jié)點都有一個值、一個左子節(jié)點和一個右子節(jié)點。(子節(jié)點可以是 null,也可以不為 null。)

遍歷樹的代碼看起來與遍歷列表的代碼非常像。如果想要 null 樹的匯總為 null,典型節(jié)點的匯總是左樹的匯總加上值的匯總和右樹的匯總,請看清單 10:


清單 10. 遍歷樹
sum_tree :: Tree Integer -> Integer
                        sum_tree Null = 0
                        sum_tree (Node left val right) = sum_tree left + val + sum_tree right
                        

在清單 10 中,第一行定義了函數(shù)使用的類型。 sum_tree :: Tree Integer -> Integer 意味著函數(shù) sum_tree 要接受一個 IntegerTree 作為參數(shù),并返回 Integer。下兩行代碼指定函數(shù)。空樹的匯總是零,其他樹的匯總是左樹加上 Integer 的值再加上右樹的值。

現(xiàn)在可以把它放在 Tree.hs 中,如清單 11 所示:


清單 11. Tree.hs
data Tree a = Null | Node (Tree a) a (Tree a)
                        sum_tree :: Tree Integer -> Integer
                        sum_tree Null = 0
                        sum_tree (Node left val right) = sum_tree left + val + sum_tree right
                        t::Tree Integer
                        t=Node Null 4 Null
                        

在這個示例中,t::Tree IntegerTree 綁定到一個類型,下一行把 t 綁定到一個值??梢杂?:l Tree 把清單 11 裝入 Hugs,并輸入 sum_tree t。也可以表示更復(fù)雜的樹,例如 Node Null 6 (Node Null 7 Null) 表示代碼一個右節(jié)點的樹。可以把不同的樹放在 t 中,然后再次運行 sum_tree 來感受一下。(請記住,每次試驗時都需要重新裝入函數(shù)。)







回顧函數(shù)性 “限制”

Haskell 能夠很好地處理數(shù)據(jù)結(jié)構(gòu)。因為把字符串看成是字符的簡單列表,所以 Haskell 不僅能很好地處理各種形式的文本,您還可以有效地處理樹、復(fù)雜的數(shù)學(xué)問題以及嵌套結(jié)構(gòu)。函數(shù)性語言被頻繁地用來構(gòu)建編譯器或者解析語言的工具,因為語言通常是用語法樹表示的。因為可以在數(shù)據(jù)結(jié)構(gòu)中包含函數(shù),所以函數(shù)性語言構(gòu)成了元編程的優(yōu)秀平臺。

現(xiàn)在已經(jīng)看到了函數(shù)性語言的基礎(chǔ),接下來可以開始了解,您要如何在沒有副作用和對狀態(tài)只有有限支持的環(huán)境下生活。正如我在前面提到過的,單體可以表示狀態(tài)的變化。但是即使用了單體,管理狀態(tài)也是困難的 —— 所以不要嘗試這樣做。相反,可以考慮通過定義不需要中間狀態(tài)的復(fù)合函數(shù)來解決問題的方式。不要考慮迭代,而應(yīng)使用遞歸或以下某個函數(shù) —— map、filterfold,它們會把表達式應(yīng)用到列表的每個元素。要使用函數(shù)性語言,只需要放棄命令式編程的風(fēng)格。學(xué)習(xí)用更加函數(shù)性的風(fēng)格進行編程,有以下好處:

  • 可以用更緊湊的方式表示思想。
  • 通過避免副作用,可以限制應(yīng)用程序某些類型的 bug 的影響。
  • 通過減少本地變量,可以讓代碼更具伸縮性。

函數(shù)性語言極大地影響了編程的思考方式。多年以來,MIT 的程序員最初使用了 Lisp,因為這種語言能夠有力地幫助程序員學(xué)習(xí)如何在更高層次的抽象上工作。

本文僅僅觸及到了函數(shù)性語言的皮毛。我鼓勵您下載 Haskell 或其他函數(shù)性語言,親自動手去體驗它。我猜,它會改造您對編程的思考方式。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多