函數(shù)式編程思維筆記

一、核心思想

函數(shù)式編程語言,讓我們用高階抽象取代基本的控制結(jié)構(gòu)。

面向?qū)ο缶幊掏ㄟ^封裝不確定因素使代碼被人理解;函數(shù)式編程通過盡量減少不確定因素使代碼被人理解。 ---- Micheal Feathers

面向?qū)ο缶幊蹋褐赜玫膯卧穷惡皖愔g的溝通消息(方法)。OOP提倡開發(fā)者針對具體問題建立專門的數(shù)據(jù)結(jié)構(gòu),以專門的操作(方法)附在數(shù)據(jù)結(jié)構(gòu)上。

函數(shù)式編程:提倡在有限幾種關(guān)鍵數(shù)據(jù)結(jié)構(gòu)(List,Map,set)上針對這些數(shù)據(jù)結(jié)構(gòu)進(jìn)行高度優(yōu)化的操作(方法),以此構(gòu)成基本運轉(zhuǎn)機(jī)制。開發(fā)者根據(jù)具體的用途插入自己的數(shù)據(jù)結(jié)構(gòu)和高階函數(shù)去調(diào)整整體機(jī)制的運轉(zhuǎn)方式。

二、思維轉(zhuǎn)變

學(xué)習(xí)新的語言,把熟悉的概念用新的語法表達(dá)出來。
學(xué)習(xí)新的范式,為熟悉的問題找到新的解答方法。

函數(shù)式編程,需要站在更高的抽象層次上工作。

命令式編程:按照程序是一系列狀態(tài)改變的命令來建模的一種編程風(fēng)格。例如 for 循環(huán) ,先確認(rèn)初始狀態(tài),每次都執(zhí)行每次迭代循環(huán)體中的一系列命令。命令式編程鼓勵將操作安排在循環(huán)內(nèi)部進(jìn)行。
函數(shù)式編程:將程序描述為表達(dá)式和變換,以數(shù)學(xué)的方式建立模型,盡量避免可變的狀態(tài)。

高階函數(shù)消除了摩擦

函數(shù)式的基本構(gòu)造單元

  • 篩選 (filter)
    需要根據(jù)篩選條件產(chǎn)生一個子集合 使用filter
  • 映射(map)
    對原集合中的每一個元素執(zhí)行給定的函數(shù),從而變成一個新的集合.
  • 折疊和化約(fold,reduce)
    用一個累積量來"收集"集合元素.比如fold的含義為用一個二元函數(shù)或者云算符結(jié)合列表的首元素和累積量的初始值,重復(fù)上一步直到列表耗盡,此時累積量的取值為折疊運算的結(jié)果.
    需要把一個集合分成一小塊一小塊的來處理,使用fold或reduce

三、權(quán)責(zé)讓渡

抽象的目的總是一樣的:讓開發(fā)者從繁瑣的細(xì)節(jié)中解脫出來,去解答問題中非重復(fù)性的那些部分。

1.迭代讓位高階函數(shù)

2.閉包

閉包:是一種奇特的函數(shù),他把需要引用的內(nèi)部變量都放在上下文里“包”起來了。換句話說,對于所需的內(nèi)部變量可以讓調(diào)用者靈活創(chuàng)建。一般閉包包括兩個步驟,創(chuàng)建綁定和執(zhí)行。
Groovy中閉包示例:

class Employee {
    def name, salary
}
//創(chuàng)建閉包
def paidMore(amount) {
    return { Employee e -> e.salary > amount }
}
//綁定閉包
isPaidMore=paidMore(10000)
//執(zhí)行閉包
def lily = new Employee("Lily", 15000)
println(isPaidMore(lily))

//綁定閉包
isPaidMore2=paidMore(20000)
//執(zhí)行閉包
def lucy = new Employee("Lucy", 20000)
println(isPaidMore2(lucy))

這里面 paidMore就是一個閉包,isPaidMore,isPaidMore2是閉包的實例。
每個閉包實例中保有自己的一份變量取值,包括私有變量也是如此。

閉包經(jīng)常被函數(shù)式語言和框架當(dāng)做一種異地執(zhí)行的機(jī)制,用來傳遞待執(zhí)行的變換代碼,例如map之類的高階函數(shù)

  • 科里化和函數(shù)的部分施用

科里化:如果你固定某些參數(shù),你將得到接受余下參數(shù)的一個函數(shù)。對于一個兩個可變量的函數(shù)xy,如果固定y=2,將得到有一個可變量的函數(shù)2x。一般情況f(x,y,z)=>f(x)(y)(z) 把函數(shù)多個參數(shù)變成一連串單個參數(shù)的變換。

部分施用:提前帶入一部分參數(shù)值,使得多個參數(shù)的函數(shù)得以省略部分參數(shù),從而得到單個參數(shù)的函數(shù)。先求解一部分參數(shù),返回由剩下參數(shù)構(gòu)成簽名的函數(shù)。一般情況,如果在f(x,y,z)上部分施用一個參數(shù),將得剩余兩個參數(shù)的函數(shù)f(y,z)。

image.png

3.科里化和部分施用一般用途

1.函數(shù)工廠
在傳統(tǒng)面向?qū)ο?/p>

image.png

2.模板方法模式
GoF的模板方法(Template Method)的用意是在固定的算法框架內(nèi)部安排一些抽象方法,為后續(xù)具體實現(xiàn)保留一部分靈活行。部分施用先固定一部分參數(shù)(注入當(dāng)前已確定的行為),留下未確認(rèn)的參數(shù)給具體實現(xiàn)去發(fā)揮,思路和模板方法如出一轍。

3.隱含參數(shù)
當(dāng)我們需要頻繁調(diào)用一個函數(shù),而每次參數(shù)值都差不多時,使用科里化來設(shè)置隱含參數(shù)?!景褏?shù)值固定】

4.遞歸

遞歸:“以一種自相似的方式來重復(fù)事物的過程”。
是運行時托付細(xì)節(jié)的一個例子,而且和函數(shù)式編程有著緊密聯(lián)系。

四. 函數(shù)的兩種常見特性

函數(shù)式編程的構(gòu)造目的:從頻繁出現(xiàn)的場景中 消滅掉煩人的細(xì)節(jié)

1.記憶

指的是在函數(shù)級別上對多次需要使用的值進(jìn)行緩存的策略.

緩存兩種用法:
  • 內(nèi)部緩存
  • 外部緩存
緩存的兩種實現(xiàn)方式
  • 手工實現(xiàn)狀態(tài)管理
  • 自動記憶機(jī)制

2.引入記憶

記憶函數(shù),使用了所謂"元函數(shù)"技法(操作的對象是函數(shù)本身,而非函數(shù)結(jié)果)

3.緩求值

緩求值的集合不會預(yù)先算好所有的元素,而是在需要用到的時候才落實下來.
有3大好處:

  • 昂貴的運算只有到了絕對必要時候才執(zhí)行
  • 可以建立無限大集合,只要一有請求,就一直送出元素
  • 按緩求值的方式來使用映射,過濾等函數(shù)可以產(chǎn)生高效代碼

4.構(gòu)造緩求值列表

對于嚴(yán)格求值的語言,如果使用閉包遞歸的將一個嚴(yán)格求值列表層層包裹起來,就可以將之變成緩求值列表.現(xiàn)實中會避免施用遞歸.

五.模式與重用

大多數(shù)現(xiàn)代語言是多范式,支持對象,函數(shù)式,元對象,以及其他多樣化范式.
在不同的范式中表現(xiàn)出來的設(shè)計模式,可能呈現(xiàn)截然不同的外在形象.這是因為函數(shù)式世界用來搭建程序的材料不一樣,所以解決問題的手段也不同.GoF的模式有一部分傳統(tǒng)模式失去了存在的意義,還有一部分,他們要解決的問題還存在,只是解決手段不一樣.

傳統(tǒng)的設(shè)計模式在函數(shù)式編程世界中大致有3中歸宿:

  • 模式已經(jīng)被吸收成語言的一部分
  • 模式中描述解決的辦法依然還成立,具體的實現(xiàn)細(xì)節(jié)有變化
  • 由于新語言或者范式獲得了原本沒有的能力,有了新的解決方案.(例如元編程)

1.函數(shù)級別的重用

復(fù)用(composition),作為一種重用機(jī)制,在函數(shù)式語言中主要表現(xiàn)為:通過參數(shù)傳遞函數(shù).與面向?qū)ο笳Z言相比,函數(shù)式語言重用發(fā)生在粗粒度的級別上,著眼提取共通的運行機(jī)制,并參數(shù)化調(diào)整其行為.

函數(shù)式的重用機(jī)制建立在列表的概念,以及可以連同上下文一期傳遞的代碼塊的概念之上.

函數(shù)式語言作為第一等成分的函數(shù),可以充當(dāng)參數(shù)和返回值.

- 函數(shù)式模板模式(Template Method)

- Strategy模式

- Flyweight 模式和記憶

- 單例模式

- Factory模式和科里化

2.結(jié)構(gòu)化重用和函數(shù)式重用的對比

1.以結(jié)構(gòu)為載體的代碼重用
如果面向?qū)ο蟮拇a值得重用,我們一般會把他提取到另一個類中,然后通過繼承來訪問它.

六.現(xiàn)實應(yīng)用

  • 函數(shù)式接口
    含有單一方法的接口,稱為函數(shù)式接口SAM(single Abstract Method),runnable 和callable接口就是有代表性的例子.在java中允許通過lambda 和SAM攜手.
  • Optional類型
    Optional 是一種結(jié)構(gòu),防止返回結(jié)果出現(xiàn)null
  • Stream

函數(shù)式的基礎(chǔ)設(shè)施

在數(shù)據(jù)庫,軟件架構(gòu)上函數(shù)是思維的作用?

1.架構(gòu)

函數(shù)式的架構(gòu)從根本上貫徹"值不可變"的思路,最大的發(fā)揮其優(yōu)點.學(xué)會從值不變的角度去思考,是我們掌握函數(shù)式思維的一條重要門徑.

- CQRS

傳統(tǒng)應(yīng)用 程序架構(gòu)把讀寫數(shù)據(jù)交織一起.例如

壞處:模型需要負(fù)責(zé)處理業(yè)務(wù)規(guī)則和驗證的工作,一般模型對象還要在內(nèi)部或通過另外的邏輯層來協(xié)調(diào)持久化事項.開發(fā)者需要兼顧讀寫兩方面潛在的影響,增加復(fù)雜性.

CQRS 通過分離架構(gòu)中負(fù)責(zé)讀取的部分和負(fù)責(zé)命令的部分,部分的簡化了程序的架構(gòu).

架構(gòu)永遠(yuǎn)是取舍的結(jié)果,CQRS簡化了一些方面,同時又復(fù)雜話了另一些方面.例如對于集中式數(shù)據(jù)庫來說,事務(wù)處理并不難.可是在CQRS架構(gòu)下,我們可能需要用最終一致性模型來取代事務(wù)性的模型.

最終一致性:是分布式中的一種模型,不對模型的變更操作施加硬性的時間限制,而只保證,當(dāng)更新發(fā)生后,模型最終回復(fù)到一致的狀態(tài).

2.Web框架

web領(lǐng)域與函數(shù)式編程簡直是天作之合.這些web框架大多具備以下共同特性:

  • 路由框架
    從應(yīng)用主體中剝離路由的相關(guān)細(xì)節(jié),將之交托專門的路由功能庫.
  • 以函數(shù)作為路由的目標(biāo)
    把路由理解成一個接收request,返回response的函數(shù).
  • 領(lǐng)域?qū)S妙A(yù)言(DSL)
    MartinFowler 將DSL定義為表達(dá)能力有限,專門針對一個狹窄問題領(lǐng)域的計算機(jī)編程語言.函數(shù)式預(yù)言偏好描述性的代碼風(fēng)格這一點恰好也常常是DSL的目的.
  • 與構(gòu)建工具緊密集成
    總體來說,不可變的值編程,會降低測試的負(fù)擔(dān),因為需要通過測試來驗證的狀態(tài)變化少了.

七.多語言與多范式

現(xiàn)代編程語言常常是多范式的,支持多種多樣的編程范式,如面向?qū)ο?元編程,函數(shù)式,過程式等等.

正交 在數(shù)學(xué)里吧兩個相互垂直的向量稱作正交,也就是說這兩個向量不相關(guān)(不會相交).在計算機(jī)科學(xué)里,把兩個組件如果沒有相互沒有任何影響(或副作用),就可以稱作是正交的.例如在groove,kotlin 中使用元編程并不妨礙我們使用函數(shù)式編程的構(gòu)造,反之亦然.

2.多范式預(yù)言的后顧之憂

由于預(yù)言支持各式各樣的抽象和概念,由不同的開發(fā)群體制作出來的庫也會呈現(xiàn)明顯的差異.不同范式的思路是不一樣的.可以運用"消費者驅(qū)動的契約",(以測試的形式)在不同團(tuán)隊之間簡歷可執(zhí)行的契約.

消費者驅(qū)動的契約:
一項集成工作的實施方與各組件供應(yīng)方共同商定的一組測試.集成方"同一",如果一方需要打破前設(shè)置條件,必須著急所受歐影響的當(dāng)事方,議定一組新的測試.

3.上下文型抽象與復(fù)合型抽象對比

復(fù)合是函數(shù)是編程領(lǐng)域奉為圭臬的設(shè)計原則.
上下文型是 基于插件的架構(gòu)使用的設(shè)計原則.

4.函數(shù)是金字塔

進(jìn)行語言的選型

語言的選型.png

不應(yīng)該從靜態(tài)類型中求索抵御錯誤的能力,從根本上擁抱函數(shù)式的概念才是正確方向.假如包括數(shù)據(jù)訪問,集成等重要職責(zé)在內(nèi)的核心api,都能以值不變性為前提來設(shè)計的話,那么所有的代碼都對大幅度地簡化.當(dāng)然在這思路下,數(shù)據(jù)庫和其他基礎(chǔ)設(shè)施的構(gòu)建方式也需要隨之放生變化.

在函數(shù)式內(nèi)核之上,用命令式語言編寫系統(tǒng)中對開發(fā)效率要求較高的部分,例如工作流,業(yè)務(wù)規(guī)則,用戶界面.最上層和原來模型一樣是 DSL層,DSL會貫穿系統(tǒng)的所有層次,一致深入到最底層.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容