C 函式式程式設計之快取技術

2021-09-07 06:14:49 字數 3254 閱讀 1516

該節我們將分成兩部分來講解,第一部分為預計算,第二部分則為快取。快取這個技術對應從事開發的人員來說是非常熟悉的,從頁面快取到資料庫快取無處不在,而其最重要的特點就是在第一次查詢後將資料快取,在以後的查詢過程中就無需重新計算而直接從記憶體中將結果返回,大大提高了效能,而我們這裡的快取則集中運用在函式上。

可能一些人並不能立馬理解這個詞的含義,所以我們就簡單的從生活例子出發介紹一下。很多人在工作中一定會這樣做事,比如上級吩咐了你一件事,但是這件事的 後半部分要等另乙個同事做好之後把對應的材料給你你才能完成。但是我們不可能一直等到那個同事完成了把材料交給我們,我們才去做這件事。而是會將這件事的 前半部分做好,那麼剩下的只要那個同事完成並交給我們,我們就直接完成下半部分就可以了。同理這樣的思維也可以用在軟體開發中,比如下面這個函式:

函式內部是利用a的值計算出c,最後再將c和b相加得出最後的結果。當然這個例子並不能完整的體現預計算的特點,但能夠讓我們理解預計算是如何實現的。假設這裡的

是乙個耗時操作,並且實際使用中會出現a的值不變動,但是b的值會經常變動的情況,但是每次呼叫這個函式都會重新根據a計算出c,那麼我們就需要一定的方式改變這個格局,這裡我們可以先嘗試採用部分應用(這裡介紹乙個函式式開發的庫->portablefcslib

這裡我們的意圖是只會計算一次c的值,而不是兩次,然後我們看看最終的輸出結果:

還是計算了兩次的c,理由很簡單,因為部分應用僅僅只是利用閉包將引數儲存了起來,只有所有引數傳遞完成後才會呼叫這個函式,並達不到預計算的效果,所以我們就需要新的方式來完成,下面我們修改dosomething函式:

這裡我們可以看到引數只剩下了a,而返回值則變成了函式,這樣才執行第一次這個方法之後將會計算出c值,由於閉包的緣故c的值就會被儲存。下面我們來看一看如何呼叫:

最後看看是不是只計算了一次c的值:

這樣我們就大功告成了,當然筆者在這裡還要再提一下,這些例子僅僅只是為了讀者能夠快速的理解,在實際的運用中還要讀者能夠根據情況靈活多變,比如這個函 數接收三個引數,但是前兩個不經常變動,但是第三個卻經常變動,並且函式的內部是根據前兩個引數計算得出乙個結果,而返回值需要根據第三個引數和這個計算 後的值得出,那麼我們就可以返回但乙個引數的函式,而函式本身需要接收兩個。

利用該技術之前我們需要理解幾個名詞,就是引用透明函式純度。這兩者都是指在我們呼叫乙個函式時,無論任何時候,只要傳遞的引數一致,返回的結果都應該是一致的。這樣的函式我們才能夠利用快取。首先我們先定義乙個函式,而這個函式將會是我們後面需要快取的函式:

然後我們修改函式使之能夠進行快取:

這裡我們可以看到我們利用了字典來對這個函式進行了快取。函式首先從字典中判斷是否存在引數a的key,如果存在直接返回計算後的結果,如果不存在則計算 該結果,並儲存到字段中,這樣我們就實現了乙個簡單的快取。下面我們來看看最終的結果,是不是確實使用了快取:

當傳遞引數10的時候,因為快取中沒有所以進行了快取,引數5也是一樣,而下一行再次計算10的時候就沒有進行計算而是直接從字典中返回了對應的結果。但是上面這種方式還存在乙個問題,如果存在多個函式都需要快取,則這個類會存在多個字段型別的字段(一些人可能會問為什麼不能共享乙個字典,這樣你就要在key的命名上花費一定的功夫,而且很容易造成重複),那麼我們就需要一種能夠不汙染類的方式來進行快取,這裡我們先介紹如何使用fcslib中的memoizer實現內部快取:

這裡我們可以看到memoizer公開了乙個getmemory的靜態方式用來獲取對應的快取物件,然後利用這個返回的物件我們就可以進行快取,最終的效果跟之前的是一樣的,我們可以看看最後控制台輸出的結果:

如果你不知道該為這個函式起什麼名字,我們可以利用反射來獲取這個函式的全稱,比如下面這個修改之後的dosomething函式就是利用了這個方式:

當然除了手動修改函式的方式,我們也可以採用自動化來使沒有利用快取的函式使用快取技術,下面我們來寫乙個函式來實現這個功能:

我們可以看到紅色框住的部分,其實就是利用了閉包,在這個函式之上又巢狀了一層函式,這樣我們就能夠進行快取了,只有在快取中不存在時才呼叫函式求值,但 是面對多個引數的情況,上面這些無法正常快取了。那麼我們就需要使用深度快取,而所謂的深度快取就是利用字典套字典來進行儲存的,比如下面這個函式,需要 傳遞兩個引數,那麼對應的快取就是:

然後就是main中進行呼叫:

最後我們可以看到下面的這個結果,第一次呼叫sfunc(10,5)時建立了快取,再第二次傳遞同樣的引數呼叫後可以看到控制台並沒有輸出對應的字串:

當然這樣字典的巢狀在引數很多的情況下,會顯得很複雜,並且也會消耗很多記憶體。但是當前也沒有非常好的解決方案,下面我們還可以利用之前寫的cache函式來實現上面這種多個引數的快取:

重點是我們紅色框住的那部分,具體的巢狀就是第乙個字典的key是第乙個引數,value就是下個函式的引用,當然這個函式是經過cache包裝之後的,那麼自然在呼叫value的函式之後自然也起到了快取作用。

C 函式式程式設計之快取技術

該節我們將分成兩部分來講解,第一部分為預計算,第二部分則為快取。快取這個技術對應從事開發的人員來說是非常熟悉的,從頁面快取到資料庫快取無處不在,而其最重要的特點就是在第一次查詢後將資料快取,在以後的查詢過程中就無需重新計算而直接從記憶體中將結果返回,大大提高了效能,而我們這裡的快取則集中運用在函式上...

C 函式式程式設計中的快取技術詳解

快取技術 該節我們將分成兩部分來講解,第一部分為預計算,第二部分則為快取。快取這個技術對應從事開發的人員來說是非常熟悉的,從頁面快取到資料庫快取無處不在,而其最重要的特點就是在第一次查詢後將資料快取,在以後的查詢過程中就無需重新計算而直接從記憶體中將結果返回,大大提高了效能,而我們這裡的快取則集中運...

函式式程式設計之純函式

純函式 我們應該還記得初中的一些數學知識,函式f的概念就是,對於輸入x產生乙個輸出y f x 這就是普通的純函式。它的定義是 相同的輸入,結果總會得到相同的輸出,而且沒有任何可觀察的 也不依賴外部環境的狀態。最常見的乙個例子就是在我們運算元組的時候slice就是純函式,splice就是不純的,看下面...