什麼是函數式編程?

General 更新 2024-05-17

到底什麼是函數式編程思維

形而上的思維:

1、數據不可變的思維:let a = 100,意義不是把100賦值給變量a,而是把a符號綁定(或者叫匹配)到100。

2、一切皆表達式思維:if b then 100 else 10,這不是條件跳轉,而是一個三元表達式。

3、函數是第一類值:函數可以作為參數傳輸,也可以作為結果返回,更可以由一個函數演化成另一個函數。

形而下的思維:

1、用遞歸替換循環。

2、難以尾遞歸的時候考慮使用延續函數(continuation)。

3、高階函數、部分應用、Lambda演算。

4、用泛型、接口、可區別聯合類型替換類繼承。

5、用二叉樹替換普通鏈表後可以支持高併發計算。

===================================================

這些也只是feature而不是思維。我想知道的是這些feature之後的邏輯。

-----------------------------------------------------------------------------------------------------

再往上說就不接地氣了,先從函數式語言說起,函數式語言其實就是模仿人的數學思維而發明的樸素,後來因為離機器太遠,不容易優化而被詬病。但科技發展到今天,編譯器的優化能力已經很強,軟件系統越來越複雜,人的分工越來越細,函數式語言離數學更近,離機器更遠,反而成為一種優勢,有助於人把問題清晰化。從這個層面看,函數式編程是一種什麼思維,就是推離機器的數學思維。這裡沒有內存、寄存器的想法,在 a=1之後,a 就不可能再等於2,當然你可以在 let a = 1 之後,再 let a = 2,但是這個a 就已經不是那個a,在停留在有內存概念的編程世界裡,a 一直是 a,它是裝東西的桶或者盒子,只是每次裡面裝的東西不同。

那麼,總的來說,是先有樸素的函數式語言,然後才有今天發現函數式編程的好處, 啟用了函數式語言的某些 feature,目的是為了把問題解構成更小的粒度。所以這些feature背後沒什麼邏輯,就好像問這石頭為什麼長這樣一樣。我只能打句偈語:本來就這樣。

Java函數式編程語言是什麼?

函數式編程語言的核心是它以處理數據的方式處理代碼。這意味著函數應該是第一等級(First-class)的值,並且能夠被賦值給變量,傳遞給函數等等。

事實上,很多函數式語言比這走得更遠,將計算和算法看得比它們操作的數據更重要。其中有些語言想分離程序狀態和函數(以一種看起來有點對立的方式,使用面向對象的語言,這通常會將它們聯繫得更緊密)。

Clojure編程語言就是一個這樣的例子,儘管它運行於基於類的Java虛擬機,Clojure的本質是函數式語言,並且在高級語言源程序中不直接公佈類和對象(儘管提供了與Java良好的互操作性)。

什麼是函數式編程思維

回答都有跑題,show概念之嫌,題主問的是函數式思維,這個問題我一直在思考,畢竟是方法論,能力有限,只能從切身實踐告訴你

1.表達式化

最初的時候,需要轉變觀念,去可變量,去循環,把命令式改成表達式,注意,這只是把你丟在荒山野嶺讓你感受一下,離開熟悉的環境,地球依然在轉,但是有個

重點,那就是一切都是表達式; 為什麼是表達式呢?這個問題就像為什麼魚在水裡?

因為函數式建立在lambda演算之上而非圖靈機,只不過兩者被證明等價,所以你可以在你的機器上跑全是表達式的代碼,就如有人證明天空適合魚生存,所以

魚可以在天上游

當你接受了魚可以在天上游之後,就該上正餐了

1.5 數據與行為分離

這也是和麵向對象不一致的地方,面向對象強調數據與行為綁定,但函數式不是,確切的說函數式 函數與數據等價,所以你才可以將函數當參數與返回值,你在設計時,切勿讓數據自己長腿能跑,其次,行為必須消除副作用,不可以偷偷把數據改了,習慣第一條後,應該不會的

2.高階邏輯

了函數式,就不要在想循環,賦值這些低階邏輯了,而應該更高階的思考問題,這比轉化表達式更難,函數式又叫聲明式,也就是你要做什麼,只要說一下就行,而

非寫個遍歷,做個狀態判斷,用函數式你不需要考慮這些,你不知道函數式的列表是怎麼遍歷的,中間向兩邊?

從後往前?這也是為何函數式適合併發的原因之一,你想知道列表中大於3的數有多少,只要,list.count(_ > 3)

而不是寫循環,你可以直接寫你的業務,不要拘泥於細節,有點像sql, 你需要什麼告訴電腦就行,你或許會問,count foreach filter

這些函數怎麼來的? 因為有了他們你才不需要寫循環,他們把你留在高階邏輯中,這個問題的答案請看下面

3.組合子邏輯 或又叫 自底向上的設計

數式和OO是反的,面向對象是自頂向下的設計,函數式是自底向上的設計,也就是先定義最基本的操作,然後不斷組合,不斷堆積以滿足你的所有需要,如sql

定義了select, from, where...這幾個組合子,來滿足你的查詢需求,同理函數式語言會提供foreach,

map等組合子(操作)來滿足你的需求,所以你必須自下而上的設計你的代碼結構,並且滿足你的需求,當你只用組合子寫代碼時,你會發現你寫的全是高階邏輯

果這些已有組合子滿足不了你,你就得自己寫,foreach不行,你就自己寫遞歸,我告訴你,遞歸背後也是組合子,這裡一些'大神'應該不知道,在圖靈機

裡,遞歸就是方法不斷調用自己沒什麼好說的,但是在lambda演算中,匿名函數是沒法調用自己的,所以遞歸是用Y組合子(又叫不動點組合子)把遞歸函數

自己求解出來再調用的,這才可以實現遞歸,並與圖靈機的循環等價,有點跑題了,總之要想順手的寫函數式,最好用面向組合子的設計,注意,不是必須,組合子

演算和lambda演算可以相互轉化,也就是,你完全可以寫一堆雜亂的表達式,但沒有組合子邏輯來得清爽,Haskell大規模使用monad這個特殊組

合子,始其變得統一整潔

好了,總結一下

函數式思維,其實就是組合子邏輯,用簡單的幾個函數組合來構建複雜邏輯,始終以高階的角度去表達問題,而非依賴副作用。

知道這點,你用java也可以寫函數式代碼了

但是,這也只是本人積累得來的感悟,絕不敢大肆伸張這就是函數式,我也在不斷研究中,如有問題,還望大神指正...

函數式編程和反應式編程有什麼區別

C語言是面向過程的編程,它的最重要特點是函數,通過主函數來調用一個個子函數。程序運行的順序都是程序員決定好了的。它是我學的第一種程序語言。 C++是面向對象的編程,類是它的主要特點,程序執行過程中,先由主函數進入,定義一些類,根據需

js 什麼是函數式編程

函數式編程是種編程典範,它將電腦運算視為函數的計算。函數編程語言最重要的基礎是 λ 演算(lambda calculus)。而且λ演算的函數可以接受函數當作輸入(參數)和輸出(返回值)。和指令式編程相比,函數式編程強調函數的計算比指令的執行重要。和過程化編程相比,函數式編程裡,函數的計算可隨時調用。

函數式編程和反應式編程有什麼區別

我暫且認為你說的RP是指Rx*框架的Reactive programming,(如果不是,就先認為是一下吧)

Rx*框架的RP,其實應該叫FRP(Functional Reactive Programming)(誤,感謝 邵成的指正,具體見補充部分),那和FP基本上就是一種派生(derive)關係了

FRP基本上就是面向異步事件流的編程了,這個異步事件流叫:Observable,一般叫:Stream

Stream就是一個 按時間排序的Events(Ongoing events ordered in time)序列

Stream是不可變(Immutability)的,任何操作都返回新的Stream, 且它是一個Monad(它有map和flatMap方法)。

FRP的關注點在Stream,而FP的關注點在(Type, Operate),Stream -> (Type, Operate)是一種泛化(generic),(Type, Operate) -> Stream 是一種派生。

RP本身是建立於觀察者模式之上的一種編程範式(級別同MV*),FP則更偏向底層解決一般化

到底什麼是函數式編程思維

“簡單說,"函數式編程"是一種"編程範式"(programming paradigm),也就是如何編寫程序的方法論。”

摘自 阮一峰先生的文章:《函數式編程初探》

就我個人理解,函數式編程是介於面向對象與面向過程的,中間性編程方法,具體且更為嚴謹的介紹,請百度“函數式編程初探”。

函數式編程要求大量的使用函數來代替變量,形成富有邏輯且簡潔易用的代碼,極端的FP和極端的OOP已經在編程界引起一定反思(FP到最後,就會導致邏輯紊亂,而OOP到最後,會導致維護的艱難)。

在這裡附上函數式編程的特點(在“函數式編程初探”中,有部分條目的具體解釋):

第一等公民是函數

帶有閉包的Lambdas/Anonymous函數

不變性,大部分無態處理,沒有狀態和變量

高併發

無副作用的調用

通過tail call實現遞歸的性能優化。

模式匹配(Haskell, Erlang)

懶賦值(Miranda, Haskell)

Homoiconicity(類似LISP)

什麼是函數式編程思維

1.表達式化

最初的時候,需要轉變觀念,去可變量,去循環,把命令式改成表達式,注意,這只是把你丟在荒山野嶺讓你感受一下,離開熟悉的環境,地球依然在轉,但是有個

重點,那就是一切都是表達式; 為什麼是表達式呢?這個問題就像為什麼魚在水裡?

因為函數式建立在lambda演算之上而非圖靈機,只不過兩者被證明等價,所以你可以在你的機器上跑全是表達式的代碼,就如有人證明天空適合魚生存,所以

魚可以在天上游

當你接受了魚可以在天上游之後,就該上正餐了

1.5 數據與行為分離

這也是和麵向對象不一致的地方,面向對象強調數據與行為綁定,但函數式不是,確切的說函數式 函數與數據等價,所以你才可以將函數當參數與返回值,你在設計時,切勿讓數據自己長腿能跑,其次,行為必須消除副作用,不可以偷偷把數據改了,習慣第一條後,應該不會的

2.高階邏輯

了函數式,就不要在想循環,賦值這些低階邏輯了,而應該更高階的思考問題,這比轉化表達式更難,函數式又叫聲明式,也就是你要做什麼,只要說一下就行,而

非寫個遍歷,做個狀態判斷,用函數式你不需要考慮這些,你不知道函數式的列表是怎麼遍歷的,中間向兩邊?

從後往前?這也是為何函數式適合併發的原因之一,你想知道列表中大於3的數有多少,只要,list.count(_ > 3)

而不是寫循環,你可以直接寫你的業務,不要拘泥於細節,有點像sql, 你需要什麼告訴電腦就行,你或許會問,count foreach filter

這些函數怎麼來的? 因為有了他們你才不需要寫循環,他們把你留在高階邏輯中,這個問題的答案請看下面

3.組合子邏輯 或又叫 自底向上的設計

數式和OO是反的,面向對象是自頂向下的設計,函數式是自底向上的設計,也就是先定義最基本的操作,然後不斷組合,不斷堆積以滿足你的所有需要,如sql

定義了select, from, where...這幾個組合子,來滿足你的查詢需求,同理函數式語言會提供foreach,

map等組合子(操作)來滿足你的需求,所以你必須自下而上的設計你的代碼結構,並且滿足你的需求,當你只用組合子寫代碼時,你會發現你寫的全是高階邏輯

果這些已有組合子滿足不了你,你就得自己寫,foreach不行,你就自己寫遞歸,我告訴你,遞歸背後也是組合子,這裡一些'大神'應該不知道,在圖靈機

裡,遞歸就是方法不斷調用自己沒什麼好說的,但是在lambda演算中,匿名函數是沒法調用自己的,所以遞歸是用Y組合子(又叫不動點組合子)把遞歸函數

自己求解出來再調用的,這才可以實現遞歸,並與圖靈機的循環等價,有點跑題了,總之要想順手的寫函數式,最好用面向組合子的設計,注意,不是必須,組合子

演算和lambda演算可以相互轉化,也就是,你完全可以寫一堆雜亂的表達式,但沒有組合子邏輯來得清爽,Haskell大規模使用monad這個特殊組

合子,始其變得統一整潔

好了,總結一下

函數式思維,其實就是組合子邏輯,用簡單的幾個函數組合來構建複雜邏輯,始終以高階的角度去表達問題,而非依賴副作用。

知道這點,你用java也可以寫函數式代碼了

函數式編程和響應式編程有什麼區別?

1. 我暫且認為你說的RP是指Rx*框架的Reactive programming,(如果不是,就先認為是一下吧)

Rx*框架的RP,其實應該叫FRP(Functional Reactive Programming)(誤,感謝 邵成的指正,具體見補充部分),那和FP基本上就是一種派生(derive)關係了

FRP基本上就是面向異步事件流的編程了,這個異步事件流叫:Observable,一般叫:Stream

Stream就是一個 按時間排序的Events(Ongoing events ordered in time)序列

Stream是不可變(Immutability)的,任何操作都返回新的Stream, 且它是一個Monad(它有map和flatMap方法)。

FRP的關注點在Stream,而FP的關注點在(Type, Operate),Stream -> (Type, Operate)是一種泛化(generic),(Type, Operate) -> Stream 是一種派生。

RP本身是建立於觀察者模式之上的一種編程範式(級別同MV*),FP則更偏向底層解決一般化問題。

如何掌握函數式編程

關於感想補充的:

我現在覺得SICP是本好教材,但是也只是本好教材。

關於路線補充的:

入門的話,不管以後想走common lisp 路線,還是走scheme這條線

我都強烈推薦把 The little schemer 和 The seasoned schemer 認真看幾遍

(the reasoned schemer我還沒看,看完接著過來更新。。。)

如果可以只看描述就把函數寫出來的話最好,做不到的話至少要把代碼打下來,給兩個值運行一下。

然後可以同時寫個簡單的解釋器,可以從垠神那篇博客或者TLS第10章的例子開始,然後學到什麼就加什麼,比如letrec , letcc之類的

關於書籍補充的:

屬於進階系列。。。

The Scheme Programming Language, 4th Edition

昨天才反應過來居然是 R. Kent Dybvig 寫的

The Scheme Programming Language, 4th Edition

作者開放的電子版

Essential of Programming Languages, 3rd Edition

Daniel P. Friedman 老爺子的。。。

以上所有書籍亞馬遜中國都有售,就是特別特別貴。。。

關於編程環境補充的:

寫著寫著就開始覺得mit-scheme不夠用了,然後我又習慣在emacs裡,不想在外面單開個解釋器。

然後發現個東西

Geiser: Top

可以在emacs裡連接racket

在部分系統裡可能有個小問題

Setting Racket Geiser Emacs Path

------------------------------------以下是第一版------------------------------------------

拋磚引玉。Haskell 和Clojure自動跳過。。。沒用過。

掌握就是不斷的練習練習思考然後接著練習。

Scheme:

1.SICP Structure and Interpretation of Computer Programs

Mit 的 6.001 麻省理工學院“開放式課程網頁” | 電機工程與計算機科學

上課視頻,講義,作業什麼的都全了。書後練習的話 我在看huangz的答案。

2.TLS The Little Schemer

我自己覺得看完SICP 前兩章跳到這本書,看完之後然後再回頭看剩下的感覺比較好。

3.當然我在扯淡

解釋器那篇比SICP的第四章簡潔多了。

4.schcme.org

Welcome to schemers.org!

如果要更深入一些的話,王垠在博客裡給過一些。

然後IU的C311還有一個書單。C311/B521/A596 Programming Languages [Home]

Dan Friedman 老爺子還是很靠的住的。

GNU有個叫gulie GNU Guile (About Guile) 的玩意。

Common lisp:

參考 田春冰河 Chun Tian (binghe)的博客,我記得他給過書單,Paul Graham 寫的之類的。...

相關問題答案
什麼是函數式編程?
什麼是響應式編程?
什麼是函數的零點?
計算機中什麼是函數?
什麼是三座標編程?
什麼是對數函數?
什麼是單項式的次數?
什麼是指數熔斷?
什麼是四核八線程?
什麼是幀數?