摘要:當我們希望能界定這二者之間的區別時,我們將第一種稱為純粹的函數式編程,后者稱為函數式編程。函數式編程我們的準則是,被稱為函數式的函數或方法都只能修改本地變量。另一種觀點支持引用透明的函數式編程,認為方法不應該有對外部可見的對象修改。
一、實現和維護系統 1.共享的可變數據
如果一個方法既不修改它內嵌類的狀態,也不修改其他對象的狀態,使用return返回所有的計算結果,那么我們稱其為純粹的或者無副作用的。
副作用就是函數的效果已經超出了函數自身的范疇。下面是一些例子。
除了構造器內的初始化操作,對類中數據結構的任何修改,包括字段的賦值操作(一個典型的例子是setter方法)。
拋出一個異常。
進行輸入/輸出操作,比如向一個文件寫數據。
從另一個角度來看“無副作用”的話,我們就應該考慮不可變對象。不可變對象是這樣一種對象,它們一旦完成初始化就不會被任何方法修改狀態。這意味著一旦一個不可變對象初始化完畢,它永遠不會進入到一個無法預期的狀態。你可以放心地共享它,無需保留任何副本,并且由于它們不會被修改,還是線程安全的。如果構成系統的各個組件都能遵守這一原則,該系統就能在完全無鎖的情況下,使用多核的并發機制。
2.聲明式編程如果你希望通過計算找出列表中最昂貴的事務,摒棄傳統的命令式編程“如何做”的風格,采用如下這種“要做什么”風格的編程通常被稱為聲明式編程(利用了函數庫,內部迭代)。
Optional3.為什么要采用函數式編程mostExpensive = transactions.stream() .max(comparing(Transaction::getValue));
使用函數式編程,你可以實現更加健壯的程序,還不會有任何的副作用。
二、什么是函數式編程對于“什么是函數式編程”這一問題最簡化的回答是“它是一種使用函數進行編程的方式”。
當談論“函數式”時,我們想說的其實是“像數學函數那樣——沒有副作用”。由此,編程上的一些精妙問題隨之而來。我們的意思是,每個函數都只能使用函數和像if-then-else這樣的數學思想來構建嗎?或者,我們也允許函數內部執行一些非函數式的操作,只要這些操作的結果不會暴露給系統中的其他部分?換句話說,如果程序有一定的副作用,不過該副作用不會為其他的調用者感知,是否我們能假設這種副作用不存在呢?調用者不需要知道,或者完全不在意這些副作用,因為這對它完全沒有影響。當我們希望能界定這二者之間的區別時,我們將第一種稱為純粹的函數式編程,后者稱為函數式編程。
1.函數式 Java 編程我們的準則是,被稱為“函數式”的函數或方法都只能修改本地變量。除此之外,它引用的對象都應該是不可修改的對象。通過這種規定,我們期望所有的字段都為final類型,所有的引用類型字段都指向不可變對象。
要被稱為函數式,函數或者方法不應該拋出任何異常。
那么,如果不使用異常,你該如何對除法這樣的函數進行建模呢?答案是請使用Optional類型
最后,作為函數式的程序,你的函數或方法調用的庫函數如果有副作用,你必須設法隱藏它們的非函數式行為,否則就不能調用這些方法。
2.引用透明性如果一個函數只要傳遞同樣的參數值,總是返回同樣的結果,那這個函數就是引用透明的。
Java語言中,關于引用透明性還有一個比較復雜的問題。假設你對一個返回列表的方法調用了兩次。這兩次調用會返回內存中的兩個不同列表,不過它們包含了相同的元素。如果這些列表被當作可變的對象值(因此是不相同的),那么該方法就不是引用透明的。如果你計劃將這些列表作為單純的值(不可修改),那么把這些值看成相同的是合理的,這種情況下該方法是引用透明的。通常情況下,在函數式編程中,你應該選擇使用引用透明的函數。
3.面向對象的編程和函數式編程的對比作為Java程序員,毫無疑問,你一定使用過某種函數式編程,也一定使用過某些我們稱為極端面向對象的編程。一種支持極端的面向對象:任何事物都是對象,程序要么通過更新字段完成操作,要么調用對與它相關的對象進行更新的方法。另一種觀點支持引用透明的函數式編程,認為方法不應該有(對外部可見的)對象修改。
三、遞歸和迭代純粹的函數式編程語言通常不包含像while或者for這樣的迭代構造器。之后你該如何編寫程序呢?比較理論的答案是每個程序都能使用無需修改的遞歸重寫,通過這種方式避免使用迭代。使用遞歸,你可以消除每步都需更新的迭代變量。
比如階乘
static long factorialStreams(long n){ return LongStream.rangeClosed(1, n) .reduce(1, (long a, long b) -> a * b); }
每次執行factorialRecursive方法調用都會在調用棧上創建一個新的棧幀,用于保存每個方法調用的狀態(即它需要進行的乘
法運算),這個操作會一直指導程序運行直到結束。這意味著你的遞歸迭代方法會依據它接收的輸入成比例地消耗內存。這也是為什么如果你使用一個大型輸入執行factorialRecursive方法,很容易遭遇StackOverflowError異常:
Exception in thread "main" java.lang.StackOverflowError
函數式語言提供了一種方法解決這一問題:尾調優化(tail-call optimization),基本的思想是你可以編寫階乘的一個迭代定義,不過迭代調用發生在函數的最后(所以我們說調用發生在尾部)。
基于“尾遞”的階乘
static long factorialTailRecursive(long n) { return factorialHelper(1, n); } static long factorialHelper(long acc, long n) { return n == 1 ? acc : factorialHelper(acc * n, n-1); }
使用棧楨方式的階乘的遞歸定義:
階乘的尾遞定義這里它只使用了一個棧幀:
壞消息是,目前Java還不支持這種優化。很多的現代JVM語言,比如Scala和Groovy都已經支持對這種形式的遞歸的優化,最終實現的效果和迭代不相上下(它們的運行速度幾乎是相同的)。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/74465.html
摘要:實戰讀書筆記第一章從方法傳遞到接著上次的,繼續來了解一下,如果繼續簡化代碼。去掉并且生成的數字是萬,所消耗的時間循序流并行流至于為什么有時候并行流效率比循序流還低,這個以后的文章會解釋。 《Java8實戰》-讀書筆記第一章(02) 從方法傳遞到Lambda 接著上次的Predicate,繼續來了解一下,如果繼續簡化代碼。 把方法作為值來傳遞雖然很有用,但是要是有很多類似與isHeavy...
摘要:本文是函數式編程第二章的讀書筆記。的語法簡化了使用匿名內部類時的模板代碼,讓程序員專注于編寫想要執行的行為,也讓代碼更加簡潔易讀。中最重要的函數接口類型推斷為新成員表達式提供了類型推斷的支持,在不需要聲明參數類型的表達式中表現的有為明顯。 本文是「Java 8 函數式編程」第二章的讀書筆記。 Lambda引入的變化 Lambda表達式,是一種緊湊的、傳遞行為的方式,從編程思想上來講,...
摘要:正確使用并行流錯用并行流而產生錯誤的首要原因,就是使用的算法改變了某些共享狀態。高效使用并行流留意裝箱有些操作本身在并行流上的性能就比順序流差還要考慮流的操作流水線的總計算成本。 一、并行流 1.將順序流轉換為并行流 對順序流調用parallel方法: public static long parallelSum(long n) { return Stream.iterate(1L...
摘要:第四章引入流一什么是流流是的新成員,它允許你以聲明性方式處理數據集合通過查詢語句來表達,而不是臨時編寫一個實現。 第四章 引入流 一、什么是流 流是Java API的新成員,它允許你以聲明性方式處理數據集合(通過查詢語句來表達,而不是臨時編寫一個實現)。就現在來說,你可以把它們看成遍歷數據集的高級迭代器。此外,流還可以透明地并行處理,你無需寫任何多線程代碼。 下面兩段代碼都是用來返回低...
摘要:限制編寫并行流,存在一些與非并行流不一樣的約定。底層框架并行流在底層沿用的框架,遞歸式的分解問題,然后每段并行執行,最終由合并結果,返回最后的值。 本書第六章的讀書筆記,也是我這個系列的最后一篇讀書筆記。后面7、8、9章分別講的測試、調試與重構、設計和架構的原則以及使用Lambda表達式編寫并發程序,因為筆記不好整理,就不寫了,感興趣的同學自己買書來看吧。 并行化流操作 關于并行與并發...
摘要:類或父類中聲明的方法的優先級高于任何聲明為默認方法的優先級。只有聲明了一個默認方法。由于比更加具體,所以編譯器會選擇中聲明的默認方法。 如果在現存的接口上引入了非常多的新方法,所有的實現類都必須進行改造,實現新方法,為了解決這個問題,Java 8為了解決這一問題引入了一種新的機制。Java 8中的接口現在支持在聲明方法的同時提供實現,這聽起來讓人驚訝!通過兩種方式可以完成這種操作。其一...
閱讀 3751·2021-09-09 09:33
閱讀 3031·2019-08-30 15:56
閱讀 3024·2019-08-30 15:56
閱讀 3315·2019-08-30 15:55
閱讀 507·2019-08-30 15:53
閱讀 2188·2019-08-30 15:52
閱讀 675·2019-08-28 18:16
閱讀 2410·2019-08-26 13:51