导读 | 本篇文章主要介绍Kotlin函数的用法,以及本身对函数式编程的一些理解。而且会和Python,C++作一些比较。 |
自从Google爸爸宣布Kotlin为本身的干儿子以后,Kotlin被各大社区炒的火热。
若是你对Kotlin语法一无所知,推荐先阅读官方文档或者中文站(https://www.kotlincn.net/docs/reference/)以后再看这篇文章会有更深入的理解。javascript
下面是维基百科上对于函数式编程的定义:html
函数式编程(英语:functional programming)或称函数程序设计,又称泛函编程,是一种编程范型,它将电脑运算视为数学上的函数计算,而且避免使用程序状态以及易变对象。函数编程语言最重要的基础是λ演算(lambda calculus)。并且λ演算的函数能够接受函数看成输入(引数)和输出(传出值)。java
下面是关于高阶函数的定义:linux
在数学和计算机科学中,高阶函数是至少知足下列一个条件的函数:接受一个或多个函数做为输入,输出一个函数android
不难推断出函数式编程最重要的基础是高阶函数。也就是支持函数能够接受函数看成输入(引数)和输出(传出值)。c++
函数做为Kotlin中的一级公民能够像其余对象同样做为函数的输入与输出,这也就是Java程序员转到Kotlin以为变化最大,最难理解的一点。若是你以前学过Python或者C++11可能会对此比较容易接受。这也是为何本文以介绍Kotlin的函数及函数式编程为主。程序员
Kotlin 函数express
下面是Kotlin中通常的函数定义,和Java不一样的是函数形参,返回值类型置后。函数体能够用等号赋值给函数定义,这里也能够看出函数和变量的平等性。编程
fun main(args: Array) { var s = sum(1,2) var m = multi(2,3) var x = maxOf(3,4) } fun sum(a: Int, b: Int): Int { return a + b } fun multi(a: Int, b: Int): Int = a * b fun maxOf(a: Int, b: Int): Int = if (a > b) a else b
另外Kotlin还支持函数默认参数,拓展函数,中缀表达式,下面是简单的例子:闭包
fun main(args: Array) { isBiggerThan(2) isBiggerThan(2, 5) var s = "a".isLetter() var a = 1 add 2 } fun isBiggerThan(a: Int, b: Int = 0) { return a > b } //拓展函数 fun String.isLetter(): Boolean { return matches(Regex("^[a-z|A-Z]$")) } //拓展函数,中缀表达式 infix fun Int.add(x: Int): Int { return this + x }
支持默认参数的函数能够减少函数的重载。
String对象中本没有判断是不是字母的方法,在Java中咱们通常会定义一些Utils方法,而在Kotlin中能够定义类的拓展函数。
第二个例子是给Int类定义了一个拓展函数,而且该拓展函数以中缀表达式表示,给予了开发者定义相似关键字的权利。
好比咱们能够这样建立一个map对象:
val kv = mapOf("a" to 1, "b" to 2)
这里的to就是一个中缀表达式,定义以下:
public infix fun<A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
Pair就是Map中存的对象,因此你也能够这样建立
val kv = mapOf(Pair("a", 1), Pair("b", 2))
在Python中若是咱们想让函数返回多个值,能够返回一个元组,Kotlin基于解构原则也能够实现相似的功能:
fun main(args: Array) { val (index, count) = findWhere("abcabcabcabc", 'c') } fun findWhere(str: String, findChar: Char): Pair<Int, Int> { var index = -1 var count = 0 for ((i, v) in str.withIndex()) { if (v == findChar) { if (index == -1) { index = i } ++count } } return Pair(index, count) }
自定义对象如何支持解构请查看官方文档,map支持解构,因此能够像下面这样遍历:
for ((k, v) in map) { print("$k -> $v, ") }
高阶函数与 Lambda 表达式
“Lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式能够表示闭包(注意和数学传统意义上的不一样)。
Python中的lambda表达式:
add = lambda x, y:x+y
C++中的lambda:
[](int x, int y) -> int{ return x + y; }
Kotlin中的lambda:
var add = {x: Int, y: Int -> x + y}
Kotlin 做为一个强类型语言仍是比较简洁的。
咱们能够这样使用一个lambda表达式:
fun main(args: Array) { val sumLambda = {a: Int, b: Int -> a + b} sumLambda(1, 2) }
它能够像函数同样使用()调用,在kotlin中操做符是能够重载的,()操做符对应的就是类的重载函数invoke()。
你还能够想下面这样定义一个变量:
val numFun: (a: Int, b: Int) -> Int
它不是一个普通的变量,它必须指向一个函数,而且函数签名必须一致:
fun main(args: Array) { val sumLambda = {a: Int, b: Int -> a + b} var numFun: (a: Int, b: Int) -> Int numFun = {a: Int, b: Int -> a + b} numFun = sumLambda numFun = ::sum numFun(1,2) } fun sum(a: Int, b: Int): Int { return a + b }
能够看到这个变量能够等于一个lambda表达式,也能够等于另外一个lambda表达式变量,还能够等于一个普通函数,可是在函数名前须要加上(::)来获取函数引用。
这个相似C++中的函数指针,然而在Python中能够直接使用函数名做为函数引用,下面是c++函数指针的例子:
#include using namespace std; void swap(int &x, int &y); int main(int arg, char* args[]) { int x = 10; int y = 20; void (*methodPtr)(int &x, int &y);//声明一个函数指针 methodPtr = &swap; //函数指针赋值 methodPtr = swap;//取地址符可省略,效果和上面一致 methodPtr(x, y); //像给函数起了一个别名,能够直接使用()调用 cout << "x:" << x << " y:" << y << endl; //x:20 y:10 } void swap(int &x, int &y) { int tmp = x; x = y; y = tmp; }
回到Kotlin,咱们还能够将一个函数传递给另外一个函数,好比:
//函数参数 fun doMap(list: List, function: (it: T) -> Any) { for (item in list) { function(item) } }
第一个参数是一个List,第二个参数是一个函数,目的就是将List中的每个元素都执行一次第二个函数。使用方法以下:
val strList = listOf("h" ,"e", "1", "a", "b", "2", " ", "", "c", "5", "7", "F") doMap(strList, {item ->print("item: ${upperLetter(item)}, ") }) fun upperLetter(item: String): String { if (item.isLetter()) { return item.toUpperCase() } return item }
第二个参数直接传进去了一个lambda表达式,固然也能够传一个函数引用:
val strList = listOf("h" ,"e", "1", "a", "b", "2", " ", "", "c", "5", "7", "F") doMap(strList, ::printUpperLetter) fun printUpperLetter(item: String) { print("item: ${upperLetter(item)}, ") } fun upperLetter(item: String): String { if (item.isLetter()) { return item.toUpperCase() } return item }
效果和上面的代码同样。
在C++中使用函数指针能够实现相似的效果:
using namespace std; void mMap(vector list, void (*fun)(int item)); int main(int arg, char* args[]) { vector list = {2,3,4,3,2,1,2}; mMap(list, [](int item) -> void { cout << item << endl; }); } void mMap(vector list, void (*fun)(int item)) { for(int it : list) { fun(it); } }
再次回到Kotlin,若是函数做为入参在入参列表的最后一个,你还能够这样作,直接写在大括号内:
fun main(args: Array) { log { sum(1,2) } } fun log(function: () -> T) { val result = function() println("result -> $result") }
是否是有点像gradle配置文件的写法,因此Kotlin能够很方便的编写 领域专用语言(DSL)
另外Kotlin还支持局部函数和函数做为返回值,看下面的代码:
fun main(args: Array) { val addResult = lateAdd(2, 4) addResult() } //局部函数,函数引用 fun lateAdd(a: Int, b: Int): Function0 { fun add(): Int { return a + b } return ::add }
在lateAdd内部定义了一个局部函数,最后返回了该局部函数的引用,对结果使用()操做符拿到最终的结果,达到延迟计算的目的。
函数做为一级公民固然能够像普通对象同样放进map中,好比下面这样:
val funs = mapOf("sum" to ::sum) val mapFun = funs["sum"] if (mapFun != null) { val result = mapFun(1,2) println("sum result -> $result") } fun sum(a: Int, b: Int): Int { return a + b }
将一个函数引用做为value放进了map中,取出来以后使用()操做符调用,能够简化一些if,else的场景。
基于以上函数式编程的特性,Kotlin能够像RxJava同样很方便的进行相应式编程,好比:
fun printUpperLetter(list: List) { list .filter (fun(item):Boolean { return item.isNotEmpty() }) .filter { item -> item.isNotBlank()} .filter { item -> if (item.isNullOrEmpty()) { return@filter false } return@filter item.matches(Regex("^[a-z|A-Z]$")) } .filter { it.isLetter() } .map(String::toUpperCase) .sortedBy { it } .forEach { print("$it, ") } println() }
上面的代码只是作演示,并没有实际意义。具体语法请查看官方文档。
我相信Kotlin做为一种强类型的现代化语言能够在保证稳定性的同时极大地提升开发者的开发效率。
原文来自:http://www.linuxprobe.com/kotlin-function-analysis.html