来源:https://pqpo.me/2017/05/25/kotlin-function/ 作者:Pqpo's Notes
自从Google爸爸宣布Kotlin为自己的干儿子之后,Kotlin被各大社区炒的火热。
函数式编程(英语:functional programming)或称函数程序设计,又称泛函编程,是一种编程范型,它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及易变对象。函数编程语言最重要的基础是λ演算(lambda calculus)。而且λ演算的函数可以接受函数当作输入(引数)和输出(传出值)。
在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:接受一个或多个函数作为输入,输出一个函数
Kotlin 函数
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++函数指针的例子: 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作为一种强类型的现代化语言可以在保证稳定性的同时极大地提高开发者的开发效率。
原文始发于微信公众号(汇编语言):Kotlin 函数与函数式编程浅析
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论