<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)方向(3):函數式編程

編程語(yǔ)言的發(fā)展趨勢及未來(lái)方向(3):函數式編程

作者: 時(shí)間:2017-04-05 來(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闡述了他眼中聲明式編程的理念及DSL,并演示C#中一種內部DSL的形式:LINQ。在這一部分中,Anders談及了聲明式編程的另一個(gè)重要組成部分:函數式編程,并使用.NET平臺上的函數式F#進(jìn)行了演示。

本文引用地址:http://dyxdggzs.com/article/201704/346198.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)始,接上篇)

    

 

  關(guān)于聲明式編程的還有一部分重要的內容,那便是函數式編程。函數式編程已經(jīng)有很長(cháng)時(shí)間的歷史了,當年LISP便是個(gè)函數式。除了LISP以外我們還有其他許多函數式,如APL、Haskell、Scheme、ML等等。關(guān)于函數式編程在學(xué)術(shù)界已經(jīng)有過(guò)許多研究了,在大約5到10年前許多人開(kāi)始吸收和整理這些研究?jì)热?,想要把它們融入更為通用的編程語(yǔ)言?,F在的編程語(yǔ)言,如C#、、Ruby、Scala等等,它們都受到了函數式編程語(yǔ)言的影響。

    

 

  我想在這里先花幾分鐘時(shí)間簡(jiǎn)單介紹一下我眼中的函數式編程語(yǔ)言。我發(fā)現很多人聽(tīng)說(shuō)過(guò)函數式編程語(yǔ)言,但還不十分清楚它們和普通的命令式編程語(yǔ)言究竟有什么區別。如今我們在使用命令式編程語(yǔ)言寫(xiě)程序時(shí),我們經(jīng)常會(huì )寫(xiě)這樣的語(yǔ)句,嗨,x等于x加一,此時(shí)我們大量依賴(lài)的是狀態(tài),可變的狀態(tài),或者說(shuō)變量,它們的值可以隨程序運行而改變。

  可變狀態(tài)非常強大,但隨之而來(lái)的便是叫做“副作用”的問(wèn)題。在使用可變狀態(tài)時(shí),你的程序則會(huì )包含副作用,比如你會(huì )寫(xiě)一個(gè)無(wú)需參數的void方法,然后它會(huì )根據你的調用次數或是在哪個(gè)線(xiàn)程上進(jìn)行調用對程序產(chǎn)生影響,因為void方法會(huì )改變程序內部的狀態(tài),從而影響之后的運行效果。

  而在函數式編程中則不會(huì )出現這個(gè)情況,因為所有的狀態(tài)都是不可變的。你可以聲明一個(gè)狀態(tài),但是不能改變這個(gè)狀態(tài)。而且由于你無(wú)法改變它,所以在函數式編程中不需要變量。事實(shí)上對函數式編程的討論更像是數學(xué)、公式,而不像是程序語(yǔ)句。如果你把x = x + 1這句話(huà)交給一個(gè)程序員看,他會(huì )說(shuō)“啊,你在增加x的值”,而如果你把它交給一個(gè)數學(xué)家看,他會(huì )說(shuō)“嗯,我知道這不是true”。

    

 

  然而,如果你給他看這條語(yǔ)言,他會(huì )說(shuō)“啊,y等于x加一,就是把x + 1的計算結果交給y,你是為這個(gè)計算指定了一個(gè)名字”。這時(shí)候在思考時(shí)就是另一種方式了,這里y不是一個(gè)變量,它只是x + 1的名稱(chēng),它不會(huì )改變,永遠代表了x + 1。

  所以在函數式編程語(yǔ)言中,當你寫(xiě)了一個(gè)函數,接受一些參數,那么當你調用這個(gè)函數時(shí),影響函數調用的只是你傳進(jìn)去的參數,而你得到的也只是計算結果。在一個(gè)純函數式編程語(yǔ)言中,函數在計算時(shí)不會(huì )對進(jìn)行一些神奇的改變,它只會(huì )使用你給它的參數,然后返回結果。在函數式編程語(yǔ)言中,一個(gè)void方法是沒(méi)有意義的,它唯一的作用只是讓你的CPU發(fā)熱,而不能給你任何東西,也不會(huì )有副作用。當然現在你可能會(huì )說(shuō),這個(gè)CPU發(fā)多少熱也是一個(gè)副作用,好吧,不過(guò)我們現在先不討論這個(gè)問(wèn)題。

    

 

  這里的關(guān)鍵在于,你解決問(wèn)題的方法和以前大不一樣了。我這里還是用代碼來(lái)說(shuō)明問(wèn)題。使用函數式語(yǔ)言寫(xiě)沒(méi)有副作用的代碼,就好比在Java或C#中使用final或是readonly的成員。

  例如這里,我們有一個(gè)Point類(lèi),構造函數接受x和y,還有一個(gè)MoveBy方法,可以把一個(gè)點(diǎn)移動(dòng)一些位置。 在傳統的命令式編程中,我們會(huì )改變Point實(shí)例的狀態(tài),這么做在平時(shí)可能不會(huì )有什么問(wèn)題。但是,如果我把一個(gè)Point對象同時(shí)交給3個(gè)API使用,然后我修改了Point,那么如何才能告訴它們狀態(tài)改變了呢?可能我們可以使用事件,blablabla,如果我們沒(méi)有事件,那么就會(huì )出現那些不愉快的副作用了。

  那么使用函數式編程的形式寫(xiě)代碼,你的Point類(lèi)還是可以包含狀態(tài),例如x和y,不過(guò)它們是readonly的,一旦初始化以后就不能改變了。MoveBy方法不能改變Point對象,它只能創(chuàng )建一個(gè)新的Point對象并返回出來(lái)。這就是一個(gè)創(chuàng )建新Point對象的函數,不是嗎?這樣就可以讓調用者來(lái)決定是使用新的還是舊的Point對象,但這里不會(huì )有產(chǎn)生副作用的情況出現。

  在函數式編程里自然不會(huì )只有Point對象,例如我們會(huì )有集合,如Dictionary,Map,List等等,它們都是不可變的。在函數式編程中,當我們向一個(gè)List里添加元素時(shí),我們會(huì )得到一個(gè)新的List,它包含了新增的元素,但之前的List依然存在。所以這些數據結構的實(shí)現方式是有根本性區別的,它們的內部結構會(huì )設法讓這類(lèi)操作變的盡可能高效。

  在函數式編程中訪(fǎng)問(wèn)狀態(tài)是十分安全的,因為狀態(tài)不會(huì )改變,我可以把一個(gè)Point或List對象交給任意多的地方去訪(fǎng)問(wèn),完全不用擔心副作用。函數式編程的十分容易并行,因為我在運行時(shí)不會(huì )修改狀態(tài),因此無(wú)論多少線(xiàn)程在運行時(shí)都可以觀(guān)察到正確的狀態(tài)。兩個(gè)函數完全無(wú)關(guān),因此它們是并行還是順序地執行便沒(méi)有什么區別了。我們還可以有延遲計算,可以進(jìn)行Memorization,這些都是函數式編程中十分有趣的方面。

  你可能會(huì )說(shuō),那么我們?yōu)槭裁床欢加眠@種方法來(lái)寫(xiě)程序呢?嗯,最終,就像我之前說(shuō)的那樣,我們不能只讓CPU發(fā)熱,我們必須要把計算結果表現出來(lái)。那么我們在屏幕上打印內容時(shí),或者把數據寫(xiě)入文件或是Socket時(shí),其實(shí)就產(chǎn)生了副作用。因此真實(shí)世界中的函數式編程,往往都是把純粹的部分進(jìn)行隔離,或是進(jìn)行更細致的控制。事實(shí)上也不會(huì )有真正純粹的函數式編程語(yǔ)言,它們都會(huì )帶來(lái)一定的副作用或是命令式編程的能力。但是,它們默認是函數式的,例如在函數式編程語(yǔ)言中,所有東西默認都是不可變的,你必須做些額外的事情才能使用可變狀態(tài)或是產(chǎn)生危險的副作用。此時(shí)你的編程觀(guān)念便會(huì )有所不同了。

    

 

  我們在自己的環(huán)境中開(kāi)發(fā)出了這樣一個(gè)函數式編程語(yǔ)言,F#,已經(jīng)包含在VS 2010中了。F#誕生于微軟劍橋研究院,由Don Syme提出,他在F#上已經(jīng)工作了5到10年了。F#使用了另一個(gè)函數式編程語(yǔ)言OCaml的常見(jiàn)核心部分,因此它是一個(gè)強類(lèi)型語(yǔ)言,并支持一些如模式匹配,類(lèi)型推斷等現代函數式編程語(yǔ)言的特性。在此之上,F#又增加了異步工作流,度量單位等較為前沿的語(yǔ)言功能。

  而F#最為重要的一點(diǎn)可能是,在我看來(lái),它是第一個(gè)和工業(yè)級的框架和工具集,如.NET和Visual Studio,有深入集成的函數式編程語(yǔ)言。F#允許你使用整個(gè).NET框架,它和C#也有類(lèi)似的執行期特征,例如強類(lèi)型,而且都會(huì )生成高效的代碼等等。我想,現在應該是展示一些F#代碼的時(shí)候了。

    

 

  首先我想先從F#中我最喜歡的特性講起,這是個(gè)F#命令行……(打開(kāi)命令行窗口以及一個(gè)F#源文件)……F#包含了一個(gè)交互式的命令行,這允許你直接輸入代碼并執行。例如輸入5……x等于5……然后x……顯示出x的值是5。然后讓sqr x等于x乘以x,于是我這里定義了一個(gè)簡(jiǎn)單的函數,名為sqr。于是我們就可以計算sqr 5等于25,sqr 10等于100。

  F#的使用方式十分動(dòng)態(tài),但事實(shí)上它是一個(gè)強類(lèi)型的編程語(yǔ)言。我們再來(lái)看看這里。這里我定義了一個(gè)計算平方和的函數sumSquares,它會(huì )遍歷每個(gè)列表中每個(gè)元素,平方后再把它們相加。讓我先用命令式的方式編寫(xiě)這個(gè)函數,再使用函數式的方式,這樣你可以看出其中的區別。

  let sumSquaresI l =

  let mutable acc = 0

  for x in l do

  acc <- acc + sqr x

  acc

  這里先是命令式的代碼,我們先創(chuàng )建一個(gè)累加器acc為0,然后遍歷列表l,把平方加到acc中,然后最后我返回acc。有幾件事情值得注意,首先為了創(chuàng )建一個(gè)可變的狀態(tài),我必須顯式地使用mutable進(jìn)行聲明,在默認情況下這是不可變的。

    

 

  還有一點(diǎn),這段代碼里我沒(méi)有提供任何的類(lèi)型信息。當我把鼠標停留在方法上時(shí),就會(huì )顯示sumSquaresI方法接受一個(gè)int序列作為參數并返回一個(gè)int。你可能會(huì )想int是哪里來(lái)的,嗯,它是由類(lèi)型推斷而來(lái)的。編譯器從這里的0發(fā)現acc必須是一個(gè)int,于是它發(fā)現這里的加號表示兩個(gè)int的相加,于是sqr函數返回的是個(gè)int,再接下來(lái)blablabla……最終它發(fā)現這里到處都是int。

    

 

  如果我把這里修改為浮點(diǎn)數0.0,鼠標再停留一下,你就會(huì )發(fā)現這個(gè)函數接受和返回的類(lèi)型都變成float了。所以這里的類(lèi)型推斷功能十分強大,也十分方便。

    

 

  現在我可以選擇這個(gè)函數,讓它在命令行里執行,然后調用sumSquaresI,提供1到100的序列,就能得到結果了。

  let rec sumSquaresF l =

  match l with

  | [] -> 0

  | h :: t -> sqr h + sumSquaresF t

  那么現在我們來(lái)?yè)Q一種函數式的風(fēng)格。這里是另一種寫(xiě)法,可以說(shuō)是純函數式的實(shí)現方式。如果你去理解這段代碼,你會(huì )發(fā)現有不少數學(xué)的感覺(jué)。這里我定義了sumSqauresF函數,輸入一個(gè)l列表,然后使用下面的模式去匹配l。如果它為空,則結果為0,否則把列表匹配為頭部和尾部,然后便將頭部的平方和尾部的平方和相加。

  你會(huì )發(fā)現,在計算時(shí)我不會(huì )去改變任何一個(gè)變量的值,我只是創(chuàng )建新的值。我這里會(huì )使用遞歸,就像在數學(xué)里我們經(jīng)常使用遞歸,把一個(gè)公式分解成幾個(gè)變化的形式,以此進(jìn)行遞歸的定義。在編程時(shí)我們也使用遞歸的做法,然后編譯器會(huì )設法幫我們轉化成尾遞歸或是循環(huán)等等。

  于是我們便可以執行sumSquaresF函數,也可以得到相同的結果。當然實(shí)際上可能你并不會(huì )像之前這樣寫(xiě)代碼,你可能會(huì )使用高階函數:

  let sumSquares l = Seq.sum (Seq.map (fun x -> x * x) l )

  例如這里,我只是把函數x乘以x映射到列表上,然后相加。這樣也可以得到相同的結果,而且這可能是更典型的做法。我這里只是想說(shuō)明,這個(gè)語(yǔ)言在編程時(shí)可能會(huì )給你帶來(lái)完全不同的感受,雖然它的執行期特征和C#比較接近。

  這便是關(guān)于F#的內容。

  (未完待續)

 



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

評論


相關(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>