自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Kotlin函數(shù)與函數(shù)式編程淺析

開(kāi)發(fā) 后端
函數(shù)作為Kotlin中的一級(jí)公民可以像其他對(duì)象一樣作為函數(shù)的輸入與輸出,這也就是Java程序員轉(zhuǎn)到Kotlin覺(jué)得變化最大,最難理解的一點(diǎn)。如果你之前學(xué)過(guò)Python或者C++11可能會(huì)對(duì)此比較容易接受。這也是為什么本文以介紹Kotlin的函數(shù)及函數(shù)式編程為主。

[[193403]]

如果你對(duì)Kotlin語(yǔ)法一無(wú)所知,推薦先閱讀官方文檔或者中文站(https://www.kotlincn.net/docs/reference/)之后再看這篇文章會(huì)有更深刻的理解。本篇文章主要介紹Kotlin函數(shù)的用法,以及自己對(duì)函數(shù)式編程的一些理解。并且會(huì)和Python,C++做一些比較。

下面是維基百科上對(duì)于函數(shù)式編程的定義:

函數(shù)式編程(英語(yǔ):functional programming)或稱(chēng)函數(shù)程序設(shè)計(jì),又稱(chēng)泛函編程,是一種編程范型,它將電腦運(yùn)算視為數(shù)學(xué)上的函數(shù)計(jì)算,并且避免使用程序狀態(tài)以及易變對(duì)象。函數(shù)編程語(yǔ)言最重要的基礎(chǔ)是λ演算(lambda calculus)。而且λ演算的函數(shù)可以接受函數(shù)當(dāng)作輸入(引數(shù))和輸出(傳出值)。

下面是關(guān)于高階函數(shù)的定義:

在數(shù)學(xué)和計(jì)算機(jī)科學(xué)中,高階函數(shù)是至少滿(mǎn)足下列一個(gè)條件的函數(shù):接受一個(gè)或多個(gè)函數(shù)作為輸入,輸出一個(gè)函數(shù)

不難推斷出函數(shù)式編程最重要的基礎(chǔ)是高階函數(shù)。也就是支持函數(shù)可以接受函數(shù)當(dāng)作輸入(引數(shù))和輸出(傳出值)。

函數(shù)作為Kotlin中的一級(jí)公民可以像其他對(duì)象一樣作為函數(shù)的輸入與輸出,這也就是Java程序員轉(zhuǎn)到Kotlin覺(jué)得變化***,最難理解的一點(diǎn)。如果你之前學(xué)過(guò)Python或者C++11可能會(huì)對(duì)此比較容易接受。這也是為什么本文以介紹Kotlin的函數(shù)及函數(shù)式編程為主。

Kotlin 函數(shù)

下面是Kotlin中一般的函數(shù)定義,和Java不同的是函數(shù)形參,返回值類(lèi)型置后。函數(shù)體可以用等號(hào)賦值給函數(shù)定義,這里也可以看出函數(shù)和變量的平等性。

  1. fun main(args: Array) { 
  2.     var s = sum(1,2) 
  3.     var m = multi(2,3) 
  4.     var x = maxOf(3,4) 
  5.  
  6. fun sum(a: Int, b: Int): Int { 
  7.     return a + b 
  8.  
  9. fun multi(a: Int, b: Int): Int = a * b 
  10.  
  11. fun maxOf(a: Int, b: Int): Int = if (a > b) a else b  

另外Kotlin還支持函數(shù)默認(rèn)參數(shù),拓展函數(shù),中綴表達(dá)式,下面是簡(jiǎn)單的例子:

  1. fun main(args: Array) { 
  2.     isBiggerThan(2) 
  3.     isBiggerThan(2, 5) 
  4.     var s = "a".isLetter() 
  5.     var a = 1 add 2 
  6.  
  7. fun isBiggerThan(a: Int, b: Int = 0) { 
  8.     return a > b 
  9.  
  10. //拓展函數(shù) 
  11. fun String.isLetter(): Boolean { 
  12.     return matches(Regex("^[a-z|A-Z]$")) 
  13.  
  14. //拓展函數(shù),中綴表達(dá)式 
  15. infix fun Int.add(x: Int): Int { 
  16.     return this + x 
  17.  

支持默認(rèn)參數(shù)的函數(shù)可以減小函數(shù)的重載。

String對(duì)象中本沒(méi)有判斷是否是字母的方法,在Java中我們一般會(huì)定義一些Utils方法,而在Kotlin中可以定義類(lèi)的拓展函數(shù)。

第二個(gè)例子是給Int類(lèi)定義了一個(gè)拓展函數(shù),并且該拓展函數(shù)以中綴表達(dá)式表示,給予了開(kāi)發(fā)者定義類(lèi)似關(guān)鍵字的權(quán)利。

比如我們可以這樣創(chuàng)建一個(gè)map對(duì)象:

  1. val kv = mapOf("a" to 1, "b" to 2) 

這里的to就是一個(gè)中綴表達(dá)式,定義如下:

  1. public infix fun<A, B> A.to(that: B): Pair<A, B> = Pair(this, that) 

Pair就是Map中存的對(duì)象,所以你也可以這樣創(chuàng)建

  1. val kv = mapOf(Pair("a", 1), Pair("b", 2)) 

在Python中如果我們想讓函數(shù)返回多個(gè)值,可以返回一個(gè)元組,Kotlin基于解構(gòu)原則也可以實(shí)現(xiàn)類(lèi)似的功能:

  1. fun main(args: Array) { 
  2.     val (indexcount) = findWhere("abcabcabcabc"'c'
  3.  
  4. fun findWhere(str: String, findChar: Char): Pair<IntInt> { 
  5.     var index = -1 
  6.     var count = 0 
  7.     for ((i, v) in str.withIndex()) { 
  8.         if (v == findChar) { 
  9.             if (index == -1) { 
  10.                 index = i 
  11.             } 
  12.             ++count 
  13.         } 
  14.     } 
  15.     return Pair(indexcount
  16.  

自定義對(duì)象如何支持解構(gòu)請(qǐng)查看官方文檔,map支持解構(gòu),所以可以像下面這樣遍歷:

  1. for ((k, v) in map) { 
  2.     print("$k -> $v, "
  3.  

高階函數(shù)與 Lambda 表達(dá)式

“Lambda 表達(dá)式”(lambda expression)是一個(gè)匿名函數(shù),Lambda表達(dá)式基于數(shù)學(xué)中的λ演算得名,直接對(duì)應(yīng)于其中的lambda抽象(lambda abstraction),是一個(gè)匿名函數(shù),即沒(méi)有函數(shù)名的函數(shù)。Lambda表達(dá)式可以表示閉包(注意和數(shù)學(xué)傳統(tǒng)意義上的不同)。

Python中的lambda表達(dá)式:

  1. add = lambda x, y:x+y 

C++中的lambda:

  1. [](int x, int y) -> intreturn x + y; } 

Kotlin中的lambda:

  1. var add = {x: Int, y: Int -> x + y} 

Kotlin 作為一個(gè)強(qiáng)類(lèi)型語(yǔ)言還是比較簡(jiǎn)潔的。

我們可以這樣使用一個(gè)lambda表達(dá)式:

  1. fun main(args: Array) { 
  2. val sumLambda = {a: Int, b: Int -> a + b} 
  3. sumLambda(1, 2) 
  4.  

它可以像函數(shù)一樣使用()調(diào)用,在kotlin中操作符是可以重載的,()操作符對(duì)應(yīng)的就是類(lèi)的重載函數(shù)invoke()。

你還可以想下面這樣定義一個(gè)變量:

  1. val numFun: (a: Int, b: Int) -> Int 

它不是一個(gè)普通的變量,它必須指向一個(gè)函數(shù),并且函數(shù)簽名必須一致:

  1. fun main(args: Array) { 
  2.     val sumLambda = {a: Int, b: Int -> a + b} 
  3.     var numFun: (a: Int, b: Int) -> Int 
  4.     numFun = {a: Int, b: Int -> a + b} 
  5.     numFun = sumLambda 
  6.     numFun = ::sum 
  7.     numFun(1,2) 
  8.  
  9. fun sum(a: Int, b: Int): Int { 
  10.     return a + b 
  11.  

可以看到這個(gè)變量可以等于一個(gè)lambda表達(dá)式,也可以等于另一個(gè)lambda表達(dá)式變量,還可以等于一個(gè)普通函數(shù),但是在函數(shù)名前需要加上(::)來(lái)獲取函數(shù)引用。

這個(gè)類(lèi)似C++中的函數(shù)指針,然而在Python中可以直接使用函數(shù)名作為函數(shù)引用,下面是c++函數(shù)指針的例子:

  1. #include  
  2.  
  3. using namespace std; 
  4.  
  5. void swap(int &x, int &y); 
  6.  
  7. int main(int arg, char* args[]) { 
  8.     int x = 10; 
  9.     int y = 20; 
  10.  
  11.     void (*methodPtr)(int &x, int &y);//聲明一個(gè)函數(shù)指針 
  12.     methodPtr = &swap; //函數(shù)指針賦值 
  13.     methodPtr = swap;//取地址符可省略,效果和上面一致 
  14.     methodPtr(x, y); //像給函數(shù)起了一個(gè)別名,可以直接使用()調(diào)用 
  15.     cout << "x:" << x << " y:" << y << endl; //x:20 y:10 
  16.  
  17. void swap(int &x, int &y) { 
  18.     int tmp = x; 
  19.     x = y; 
  20.     y = tmp; 
  21.  

回到Kotlin,我們還可以將一個(gè)函數(shù)傳遞給另一個(gè)函數(shù),比如:

  1. //函數(shù)參數(shù) 
  2. fun  doMap(list: List, function: (it: T) -> Any) { 
  3.     for (item in list) { 
  4.         function(item) 
  5.     } 
  6.  

***個(gè)參數(shù)是一個(gè)List,第二個(gè)參數(shù)是一個(gè)函數(shù),目的就是將List中的每一個(gè)元素都執(zhí)行一次第二個(gè)函數(shù)。使用方法如下:

  1. val strList = listOf("h" ,"e""1""a""b""2"" """"c""5""7""F"
  2. doMap(strList, {item ->print("item: ${upperLetter(item)}, ") }) 
  3.  
  4. fun upperLetter(item: String): String { 
  5.     if (item.isLetter()) { 
  6.         return item.toUpperCase() 
  7.     } 
  8.     return item 
  9.  

第二個(gè)參數(shù)直接傳進(jìn)去了一個(gè)lambda表達(dá)式,當(dāng)然也可以傳一個(gè)函數(shù)引用:

  1. val strList = listOf("h" ,"e""1""a""b""2"" """"c""5""7""F"
  2. doMap(strList, ::printUpperLetter) 
  3.  
  4. fun printUpperLetter(item: String) { 
  5.     print("item: ${upperLetter(item)}, "
  6.  
  7. fun upperLetter(item: String): String { 
  8.     if (item.isLetter()) { 
  9.         return item.toUpperCase() 
  10.     } 
  11.     return item 
  12.  

效果和上面的代碼一樣。

在C++中使用函數(shù)指針可以實(shí)現(xiàn)類(lèi)似的效果:

  1. using namespace std; 
  2.  
  3. void mMap(vector list, void (*fun)(int item)); 
  4.  
  5. int main(int arg, char* args[]) { 
  6.     vector list = {2,3,4,3,2,1,2}; 
  7.     mMap(list, [](int item) -> void { cout << item << endl; }); 
  8.  
  9. void mMap(vector list, void (*fun)(int item)) { 
  10.     for(int it : list) { 
  11.         fun(it); 
  12.     } 
  13.  

再次回到Kotlin,如果函數(shù)作為入?yún)⒃谌雲(yún)⒘斜淼?**一個(gè),你還可以這樣做,直接寫(xiě)在大括號(hào)內(nèi):

  1. fun main(args: Array) { 
  2.     log { sum(1,2) } 
  3.  
  4. fun  log(function: () -> T) { 
  5.     val result = function() 
  6.     println("result -> $result"
  7.  

是不是有點(diǎn)像gradle配置文件的寫(xiě)法,所以Kotlin可以很方便的編寫(xiě) 領(lǐng)域?qū)S谜Z(yǔ)言(DSL)

另外Kotlin還支持局部函數(shù)和函數(shù)作為返回值,看下面的代碼:

  1. fun main(args: Array) { 
  2.     val addResult = lateAdd(2, 4) 
  3.     addResult() 
  4. //局部函數(shù),函數(shù)引用 
  5. fun lateAdd(a: Int, b: Int): Function0 { 
  6.     fun add(): Int { 
  7.         return a + b 
  8.     } 
  9.     return ::add 
  10.  

在lateAdd內(nèi)部定義了一個(gè)局部函數(shù),***返回了該局部函數(shù)的引用,對(duì)結(jié)果使用()操作符拿到最終的結(jié)果,達(dá)到延遲計(jì)算的目的。

函數(shù)作為一級(jí)公民當(dāng)然可以像普通對(duì)象一樣放進(jìn)map中,比如下面這樣:

  1. val funs = mapOf("sum" to ::sum
  2. val mapFun = funs["sum"
  3. if (mapFun != null) { 
  4.    val result = mapFun(1,2) 
  5.    println("sum result -> $result"
  6.  
  7. fun sum(a: Int, b: Int): Int { 
  8.     return a + b 
  9.  

將一個(gè)函數(shù)引用作為value放進(jìn)了map中,取出來(lái)之后使用()操作符調(diào)用,可以簡(jiǎn)化一些if,else的場(chǎng)景。

基于以上函數(shù)式編程的特性,Kotlin可以像RxJava一樣很方便的進(jìn)行相應(yīng)式編程,比如:

  1. fun printUpperLetter(list: List) { 
  2.     list 
  3.             .filter (fun(item):Boolean { 
  4.                 return item.isNotEmpty() 
  5.             }) 
  6.             .filter { item -> item.isNotBlank()} 
  7.             .filter { 
  8.                 item -> 
  9.                 if (item.isNullOrEmpty()) { 
  10.                     return@filter false 
  11.                 } 
  12.                 return@filter item.matches(Regex("^[a-z|A-Z]$")) 
  13.             } 
  14.             .filter { it.isLetter() } 
  15.             .map(String::toUpperCase) 
  16.             .sortedBy { it } 
  17.             .forEach { print("$it, ") } 
  18.     println() 
  19.  

上面的代碼只是做演示,并無(wú)實(shí)際意義。具體語(yǔ)法請(qǐng)查看官方文檔。

我相信Kotlin作為一種強(qiáng)類(lèi)型的現(xiàn)代化語(yǔ)言可以在保證穩(wěn)定性的同時(shí)極大地提高開(kāi)發(fā)者的開(kāi)發(fā)效率。

責(zé)任編輯:龐桂玉 來(lái)源: Android開(kāi)發(fā)中文站
相關(guān)推薦

2023-10-07 00:01:02

Java函數(shù)

2022-07-07 09:03:36

Python返回函數(shù)匿名函數(shù)

2013-09-09 09:41:34

2016-10-19 14:35:20

JavaScript函數(shù)式編程

2011-03-08 15:47:32

函數(shù)式編程

2016-10-31 20:46:22

函數(shù)式編程Javascript

2025-03-11 10:00:20

Golang編程函數(shù)

2020-09-24 10:57:12

編程函數(shù)式前端

2023-12-14 15:31:43

函數(shù)式編程python編程

2011-08-24 09:13:40

編程

2022-09-22 08:19:26

WebFlux函數(shù)式編程

2010-03-11 10:34:22

Scala

2012-09-21 09:21:44

函數(shù)式編程函數(shù)式語(yǔ)言編程

2020-09-23 07:50:45

Java函數(shù)式編程

2020-09-22 11:00:11

Java技術(shù)開(kāi)發(fā)

2016-08-11 10:11:07

JavaScript函數(shù)編程

2016-08-11 10:34:37

Javascript函數(shù)編程

2020-09-04 15:04:17

函數(shù)式編程程序員函數(shù)

2019-09-09 11:40:18

編程函數(shù)開(kāi)發(fā)

2010-11-25 09:06:37

Web開(kāi)發(fā)函數(shù)式編程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)