Ⅰ 函數在編程中扮演著什麼樣的作用
函數在編程中的作用:
支持閉包和高階函數,支持惰性計算(lazy evaluation)。使用遞歸作為控制流程的機制。加強了引用透明性。沒有副作用。我將重點放在在 java 語言中使用閉包和高階函數上,但是首先對上面列出的所有特點做一個概述。
閉包和高階函數
函數編程支持函數作為第一類對象,有時稱為 閉包或者 仿函數(functor)對象。實質上,閉包是起函數的作用並可以像對象一樣操作的對象。與此類似,FP 語言支持 高階函數。高階函數可以用另一個函數(間接地,用一個表達式) 作為其輸入參數,在某些情況下,它甚至返回一個函數作為其輸出參數。這兩種結構結合在一起使得可以用優雅的方式進行模塊化編程,這是使用 FP 的最大好處。
惰性計算
除了高階函數和仿函數(或閉包)的概念,FP 還引入了惰性計算的概念。在惰性計算中,表達式不是在綁定到變數時立即計算,而是在求值程序需要產生表達式的值時進行計算。延遲的計算使您可以編寫可能潛在地生成無窮輸出的函數。因為不會計算多於程序的其餘部分所需要的值,所以不需要擔心由無窮計算所導致的 out-of-memory 錯誤。一個惰性計算的例子是生成無窮 Fibonacci 列表的函數,但是對 第 n 個Fibonacci 數的計算相當於只是從可能的無窮列表中提取一項。
遞歸
FP 還有一個特點是用遞歸做為控制流程的機制。例如,Lisp 處理的列表定義為在頭元素後面有子列表,這種表示法使得它自己自然地對更小的子列表不斷遞歸。
函數的優點:
1.代碼簡潔,開發快速
函數式編程大量使用函數,減少了代碼的重復,因此程序比較短,開發速度較快。
Paul Graham在《黑客與畫家》一書中寫道:同樣功能的程序,極端情況下,Lisp代碼的長度可能是C代碼的二十分之一。
如果程序員每天所寫的代碼行數基本相同,這就意味著,"C語言需要一年時間完成開發某個功能,Lisp語言只需要不到三星期。反過來說,如果某個新功能,Lisp語言完成開發需要三個月,C語言需要寫五年。"當然,這樣的對比故意誇大了差異,但是"在一個高度競爭的市場中,即使開發速度只相差兩三倍,也足以使得你永遠處在落後的位置。"
2. 接近自然語言,易於理解
函數式編程的自由度很高,可以寫出很接近自然語言的代碼。
前文曾經將表達式(1 + 2) * 3 - 4,寫成函數式語言:
subtract(multiply(add(1,2), 3), 4)
對它進行變形,不難得到另一種寫法:
add(1,2).multiply(3).subtract(4)
這基本就是自然語言的表達了。再看下面的代碼,大家應該一眼就能明白它老戚哪的意思吧:
merge([1,2],[3,4]).sort().search("2")
因此,函數式編程的代碼更容易理解。
3. 更方便的代碼管理
函數式編程不依賴、也不會改變外界的狀態,只要給定輸入參數,返回的結果必定相同。因此,每一個函數都可以被看做獨立單元,很有利於進行單元測試(unit testing)和除錯(debugging),以及模塊化組合。
4. 易於"並發編程"
函數式編程不需要考慮"死鎖"(deadlock),因為它不修改變數,所以根本不存在"鎖"線程的問題。不必擔心一個線程的數據,被另一個線程修改,所以侍碼可以很放心地把工作分攤到多個線程,部署"並發編程"仔棗(concurrency)。
請看下面的代碼:
var s1 = Op1();
var s2 = Op2();
var s3 = concat(s1, s2);
由於s1和s2互不幹擾,不會修改變數,誰先執行是無所謂的,所以可以放心地增加線程,把它們分配在兩個線程上完成。其他類型的語言就做不到這一點,因為s1可能會修改系統狀態,而s2可能會用到這些狀態,所以必須保證s2在s1之後運行,自然也就不能部署到其他線程上了。
多核CPU是將來的潮流,所以函數式編程的這個特性非常重要。
5. 代碼的熱升級
函數式編程沒有副作用,只要保證介面不變,內部實現是外部無關的。所以,可以在運行狀態下直接升級代碼,不需要重啟,也不需要停機。Erlang語言早就證明了這一點,它是瑞典愛立信公司為了管理電話系統而開發的,電話系統的升級當然是不能停機的。
Ⅱ java 為什麼引入函數式介面而不是委託
一、Lambda表達式Lambda表達式可以說是Java 8最大的賣點,她將函數式編程引入了Java。Lambda允許把函數作為一個方法的參數,或者把代碼看成數據。一個Lambda表達式可以由用逗號分隔的參數列表、–符號與函數體三部分表示。例如:Arrays.asList( "p", "k", "u","f", "o", "r","k").forEach( e - System.out.println( e ) ); 1 Arrays.asList( "p", "k", "u","f", "o", "r","k").forEach( e - System.out.println( e ) ); 為了使現有函數更好的支持Lambda表達式,Java 8引入了函數式介面的概念。函數式介面就是只有一個方法的普通介面。java.lang.Runnable與java.util.concurrent.Callable是函數式介面最典型的例子。為此,Java 8增加了一種特殊的註解@FunctionalInterface:1 @FunctionalInterface2 public interface Functional {3 void method();4 }二、介面的默認方法與靜態方法我們可以在介面中定義默認方法,使用default關鍵字,並提供默認的實現。所有實現這個介面的類都會接受默認方法的實現,除非子類提供的自己的實現。例如:1 public interface DefaultFunctionInterface {2 default String defaultFunction() {3 return "default function";4 }5 }我們還可以在介面中定義靜態方法,使用static關鍵字,也可以提供實現。例如:1 public interface StaticFunctionInterface {2 static String staticFunction() {3 return "static function";4 }5 }介面的默認方法和靜態方法的引入,其實可以認為引入了C++中抽象類的理念,以後我們再也不用在每個實現類中都寫重復的代碼了。三、方法引用通常與Lambda表達式聯合使用,可以直接引用已有Java類或對象的方法。一般有四種不同的方法引用:構造器引用。語法是Class::new,或者更一般的Class T ::new,要求構造器方法是沒有參數;靜態方法引用。語法是Class::static_method,要求接受一個Class類型的參數;特定類的任意對象方法引用。它的語法是Class::method。要求方法是沒有參數的;特定對象的方法引用,它的語法是instance::method。要求方法接受一個參數,與3不同的地方在於,3是在列表元素上分別調用方法,而4是在某個對象上調用方法,將列表元素作為參數傳入;四、重復註解在Java 5中使用註解有一個限制,即相同的註解在同一位置只能聲明一次。Java 8引入重復註解,這樣相同的註解在同一地方也可以聲明多次。重復註解機制本身需要用@Repeatable註解。Java 8在編譯器層做了優化,相同註解會以集合的方式保存,因此底層的原理並沒有變化。五、擴展註解的支持Java 8擴展了註解的上下文,幾乎可以為任何東西添加註解,包括局部變數、泛型類、父類與介面的實現,連方法的異常也能添加註解。六、OptionalJava 8引入Optional類來防止空指針異常,Optional類最先是由Google的Guava項目引入的。Optional類實際上是個容器:它可以保存類型T的值,或者保存null。使用Optional類我們就不用顯式進行空指針檢查了。七、StreamStream API是把真正的函數式編程風格引入到Java中。其實簡單來說可以把Stream理解為MapRece,當然Google的MapRece的靈感也是來自函數式編程。她其實是一連串支持連續、並行聚集操作的元素。從語法上看,也很像linux的管道、或者鏈式編程,代碼寫起來簡潔明了,非常酷帥!八、Date/Time API (JSR 310)Java 8新的Date-Time API (JSR 310)受Joda-Time的影響,提供了新的java.time包,可以用來替代 java.util.Date和java.util.Calendar。一般會用到Clock、LocaleDate、LocalTime、LocaleDateTime、ZonedDateTime、Duration這些類,對於時間日期的改進還是非常不錯的。九、JavaScript引擎NashornNashorn允許在JVM上開發運行JavaScript應用,允許Java與JavaScript相互調用。十、Base64在Java 8中,Base64編碼成為了Java類庫的標准。Base64類同時還提供了對URL、MIME友好的編碼器與解碼器。除了這十大新特性之外,還有另外的一些新特性:更好的類型推測機制:Java 8在類型推測方面有了很大的提高,這就使代碼更整潔,不需要太多的強制類型轉換了。編譯器優化:Java 8將方法的參數名加入了位元組碼中,這樣在運行時通過反射就能獲取到參數名,只需要在編譯時使用-parameters參數。並行(parallel)數組:支持對數組進行並行處理,主要是parallelSort()方法,它可以在多核機器上極大提高數組排序的速度。並發(Concurrency):在新增Stream機制與Lambda的基礎之上,加入了一些新方法來支持聚集操作。Nashorn引擎jjs:基於Nashorn引擎的命令行工具。它接受一些JavaScript源代碼為參數,並且執行這些源代碼。類依賴分析器jdeps:可以顯示Java類的包級別或類級別的依賴。JVM的PermGen空間被移除:取代它的是Metaspace(JEP 122)。
Ⅲ 什麼是函數式編程思維
回答都有跑題,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也可以寫函數式代碼了
但是,這也只是本人積累得來的感悟,絕不敢大肆伸張這就是函數式,我也在不斷研究中,如有問題,還望大神指正
Ⅳ 函數式編程-Lambda與Stream
我們在創建線程並啟動時可以使用匿名內部類的寫法:
可以使用Lambda的格式對其進行修改。修改後如下:
現有方法定義如下,其中IntBinaryOperator是一個介面。先使用匿名內部類的寫法調用該方法。
Lambda寫法:
現有方法定義如下,其中IntPredicate是一個介面。先使用匿名內部類的寫法調用該方法。
Lambda寫法:
現有方法定義如下,其中Function是一個介面。先使用匿名內部類的寫法調用卜行握該方法。
Lambda寫法:
現有方法定義如下,其中IntConsumer是一個介面。先使用匿名內部類的寫法調用該方法。
Lambda寫法:
Java8的Stream使用的是函數式編程模式,如同它的名字一樣,它可以被用來對集合或數組進行鏈狀流式的操作。可以更方便的讓我們對集合或數組操作。
我們可以調用getAuthors方法獲取到作家的集合。現在需要列印所有年齡小於18的作家的名字,並且要注意去重。
單列集合: 集合對象.stream()
數組:Arrays.stream(數組)或者使用Stream.of來創建
雙列集合:轉換成單列集合後再創建
可以對流中的元素進行條件過濾,符合過濾條件的才能繼續留在流中。
例如:
列印所有姓名長度大於1的作家的姓名
可以把對流中的元素進行計算或轉換。
例如:
列印所有作家的姓名
可以去除流中的重復元素。
例如:
列印所有作家的姓名,並且要求其中不能有重復元素。
注意:distinct方法是依賴Object的equals方法來判斷是否是相同對象的。所以需要注意重寫equals方法。
可以對流中的元素進行排序。
例如:
對流中的元素按照年齡進行降序排序,並且要求不能有重復的元素。
注意:如果調用空參的sorted()方法,需要流中的元素是實現了Comparable。
可以設置流的最大長度,超出的部分將被拋棄。帶昌
例如:
對流中的元素按照年齡進行降序排序,並且要求不能有重復的元素,然後列印其中年齡最大的兩個作家的姓名。
跳過流中的前n個元素,返回剩下的元素
例如:
列印除了年齡最大的作家外的其他作家,要求不能有重復元素,並且按照年齡降序排序。
map只能把一個對象轉換成另一個對象來作為流中的元素。而flatMap可以把一個對象轉換成多個對象作為流中的元素。
例一:
列印所有書籍的名字。要求對重復的元素進行去重。
例二:
列印現有數據的所有分類。要求對分類進行去重。不能出現這種格式:哲學,愛情
對流中的元素進行遍歷操作,我們通過傳入的型慶參數去指定對遍歷到的元素進行什麼具體操作。
例子:
輸出所有作家的名字
可以用來獲取當前流中元素的個數。
例子:
列印這些作家的所出書籍的數目,注意刪除重復元素。
可以用來或者流中的最值。
例子:
分別獲取這些作家的所出書籍的最高分和最低分並列印。
把當前流轉換成一個集合。
例子:
獲取一個存放所有作者名字的List集合。
獲取一個所有書名的Set集合。
獲取一個Map集合,map的key為作者名,value為List
可以用來判斷是否有任意符合匹配條件的元素,結果為boolean類型。
例子:
判斷是否有年齡在29以上的作家
可以用來判斷是否都符合匹配條件,結果為boolean類型。如果都符合結果為true,否則結果為false。
例子:
判斷是否所有的作家都是成年人
可以判斷流中的元素是否都不符合匹配條件。如果都不符合結果為true,否則結果為false
例子:
判斷作家是否都沒有超過100歲的。
獲取流中的任意一個元素。該方法沒有辦法保證獲取的一定是流中的第一個元素。
例子:
獲取任意一個年齡大於18的作家,如果存在就輸出他的名字
獲取流中的第一個元素。
例子:
獲取一個年齡最小的作家,並輸出他的姓名。
對流中的數據按照你指定的計算方式計算出一個結果。(縮減操作)
rece的作用是把stream中的元素給組合起來,我們可以傳入一個初始值,它會按照我們的計算方式依次拿流中的元素和初始化值進行計算,計算結果再和後面的元素計算。
rece兩個參數的重載形式內部的計算方式如下:
其中identity就是我們可以通過方法參數傳入的初始值,accumulator的apply具體進行什麼計算也是我們通過方法參數來確定的。
例子:
使用rece求所有作者年齡的和
使用rece求所有作者中年齡的最大值
使用rece求所有作者中年齡的最小值
rece一個參數的重載形式內部的計算
如果用一個參數的重載方法去求最小值代碼如下:
我們在編寫代碼的時候出現最多的就是空指針異常。所以在很多情況下我們需要做各種非空的判斷。
例如:
尤其是對象中的屬性還是一個對象的情況下。這種判斷會更多。
而過多的判斷語句會讓我們的代碼顯得臃腫不堪。
所以在JDK8中引入了Optional,養成使用Optional的習慣後你可以寫出更優雅的代碼來避免空指針異常。
並且在很多函數式編程相關的API中也都用到了Optional,如果不會使用Optional也會對函數式編程的學習造成影響。
Optional就好像是包裝類,可以把我們的具體數據封裝Optional對象內部。然後我們去使用Optional中封裝好的方法操作封裝進去的數據就可以非常優雅的避免空指針異常。
我們一般使用 Optional 的 靜態方法ofNullable 來把數據封裝成一個Optional對象。無論傳入的參數是否為null都不會出現問題。
你可能會覺得還要加一行代碼來封裝數據比較麻煩。但是如果改造下getAuthor方法,讓其的返回值就是封裝好的Optional的話,我們在使用時就會方便很多。
而且在實際開發中我們的數據很多是從資料庫獲取的。Mybatis從3.5版本可以也已經支持Optional了。我們可以直接把方法的返回值類型定義成Optional類型,MyBastis會自己把數據封裝成Optional對象返回。封裝的過程也不需要我們自己操作。
如果你 確定一個對象不是空 的則可以使用 Optional 的 靜態方法of 來把數據封裝成Optional對象。
但是一定要注意,如果使用of的時候傳入的參數必須不為null。(嘗試下傳入null會出現什麼結果)
如果一個方法的返回值類型是Optional類型。而如果我們經判斷發現某次計算得到的返回值為null,這個時候就需要把null封裝成Optional對象返回。這時則可以使用 Optional 的 靜態方法empty 來進行封裝。
所以最後你覺得哪種方式會更方便呢? ofNullable
我們獲取到一個Optional對象後肯定需要對其中的數據進行使用。這時候我們可以使用其 ifPresent 方法對來消費其中的值。
這個方法會判斷其內封裝的數據是否為空,不為空時才會執行具體的消費代碼。這樣使用起來就更加安全了。
例如,以下寫法就優雅的避免了空指針異常。
如果我們想獲取值自己進行處理可以使用get方法獲取,但是不推薦。因為當Optional內部的數據為空的時候會出現異常。
如果我們期望安全的獲取值。我們不推薦使用get方法,而是使用Optional提供的以下方法。
我們可以使用filter方法對數據進行過濾。如果原本是有數據的,但是不符合判斷,也會變成一個無數據的Optional對象。
我們可以使用isPresent方法進行是否存在數據的判斷。如果為空返回值為false,如果不為空,返回值為true。但是這種方式並不能體現Optional的好處, 更推薦使用ifPresent方法 。
Optional還提供了map可以讓我們的對數據進行轉換,並且轉換得到的數據也還是被Optional包裝好的,保證了我們的使用安全。
例如我們想獲取作家的書籍集合。
只有一個抽象方法 的介面我們稱之為函數介面。
JDK的函數式介面都加上了 @FunctionalInterface 註解進行標識。但是無論是否加上該註解只要介面中只有一個抽象方法,都是函數式介面。
我們在使用lambda時,如果方法體中只有一個方法的調用的話(包括構造方法),我們可以用方法引用進一步簡化代碼。
我們在使用lambda時不需要考慮什麼時候用方法引用,用哪種方法引用,方法引用的格式是什麼。我們只需要在寫完lambda方法發現方法體只有一行代碼,並且是方法的調用時使用快捷鍵嘗試是否能夠轉換成方法引用即可。
當我們方法引用使用的多了慢慢的也可以直接寫出方法引用。
類名或者對象名::方法名
其實就是引用類的靜態方法
如果我們在重寫方法的時候,方法體中 只有一行代碼 ,並且這行代碼是 調用了某個類的靜態方法 ,並且我們把要重寫的 抽象方法中所有的參數都按照順序傳入了這個靜態方法中 ,這個時候我們就可以引用類的靜態方法。
例如:
如下代碼就可以用方法引用進行簡化
注意,如果我們所重寫的方法是沒有參數的,調用的方法也是沒有參數的也相當於符合以上規則。
優化後如下:
如果我們在重寫方法的時候,方法體中 只有一行代碼 ,並且這行代碼是 調用了某個對象的成員方法 ,並且我們把要重寫的 抽象方法中所有的參數都按照順序傳入了這個成員方法中 ,這個時候我們就可以引用對象的實例方法
例如:
優化後:
如果我們在重寫方法的時候,方法體中 只有一行代碼 ,並且這行代碼是 調用了第一個參數的成員方法 ,並且我們把要 重寫的抽象方法中剩餘的所有的參數都按照順序傳入了這個成員方法中 ,這個時候我們就可以引用類的實例方法。
例如:
優化後如下:
如果方法體中的一行代碼是構造器的話就可以使用構造器引用。
如果我們在重寫方法的時候,方法體中 只有一行代碼 ,並且這行代碼是 調用了某個類的構造方法 ,並且我們把 要重寫的抽象方法中的所有的參數都按照順序傳入了這個構造方法中 ,這個時候我們就可以引用構造器。
例如:
優化後:
我們之前用到的很多Stream的方法由於都使用了泛型。所以涉及到的參數和返回值都是引用數據類型。
即使我們操作的是整數小數,但是實際用的都是他們的包裝類。JDK5中引入的自動裝箱和自動拆箱讓我們在使用對應的包裝類時就好像使用基本數據類型一樣方便。但是你一定要知道裝箱和拆箱肯定是要消耗時間的。雖然這個時間消耗很下。但是在大量的數據不斷的重復裝箱拆箱的時候,你就不能無視這個時間損耗了。
所以為了讓我們能夠對這部分的時間消耗進行優化。Stream還提供了很多專門針對基本數據類型的方法。
例如:mapToInt,mapToLong,mapToDouble,flatMapToInt,flatMapToDouble等。
當流中有大量元素時,我們可以使用並行流去提高操作的效率。其實並行流就是把任務分配給多個線程去完全。如果我們自己去用代碼實現的話其實會非常的復雜,並且要求你對並發編程有足夠的理解和認識。而如果我們使用Stream的話,我們只需要修改一個方法的調用就可以使用並行流來幫我們實現,從而提高效率。
parallel方法可以把串列流轉換成並行流。
也可以通過parallelStream直接獲取並行流對象。
Ⅳ java支持的編程範式
Java編程範式
1.命令式編程
核心內容就是:「用語句更改程序的狀態」
大多數流行的編程語言都或多或少基於命令式編程發展而來,命令式語言最典型的實例就是C語言
2.面向對象編程
面向對象編程經常與命令式編程聯系在一起,在實踐當中,兩者是可以共存的。Java就是這種協作的生動證明
面向對象基於四個基本原則:封裝、繼承、多態、抽象
3.聲明式編程
與命令式編程相反,聲明式編程它指定程序應該做什麼,而不具體說明怎麼做。
純粹的聲明式語言包括資料庫查詢語言(如SQL和Xpath)以及正則表達式。
與命令式編程語言相比,聲明式編程語言更加抽象,它們並不模擬硬體結構,因此不會改變程序狀態,而是將它們轉換為新狀態,並且更接近數學邏輯
通常,非命令是的編程範式都被認為屬於聲明式類別。
4.函數式編程
函數式編程是聲明式編程的子範式,與命令式編程相反,函數式變成不會改變程序的內部狀態。
在函數式編程術語中,函數類似於數學函數,函數的輸出僅依賴於其參數,而不管程序的狀態如何,完全不受函數式是何時執行的影響
函數式語言受歡迎的原因之一是它們可以輕松的在並行環境中運行,這與多線程不太一樣,函數式語言支持並行的關鍵在於它們的基本原理:函數僅依賴與輸入參數而不依賴於程序的狀態。它們可以在任何地方運行,然後將多個並行執行的結果連接起來並進一步使用