Kotlin 函数与函数式编程浅析

发表于 2年以前  | 总阅读数:320 次

如果你对Kotlin语法一无所知,推荐先阅读官方文档或者中文站(https://www.kotlincn.net/docs/reference/)之后再看这篇文章会有更深刻的理解。本篇文章主要介绍Kotlin函数的用法,以及自己对函数式编程的一些理解。并且会和Python,C++做一些比较。下面是维基百科上对于函数式编程的定义:

函数式编程(英语:functional programming)或称函数程序设计,又称泛函编程,是一种编程范型,它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及易变对象。函数编程语言最重要的基础是λ演算(lambda calculus)。而且λ演算的函数可以接受函数当作输入(引数)和输出(传出值)。

下面是关于高阶函数的定义:

在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:接受一个或多个函数作为输入,输出一个函数

不难推断出函数式编程最重要的基础是高阶函数。也就是支持函数可以接受函数当作输入(引数)和输出(传出值)。函数作为Kotlin中的一级公民可以像其他对象一样作为函数的输入与输出,这也就是Java程序员转到Kotlin觉得变化最大,最难理解的一点。如果你之前学过Python或者C++11可能会对此比较容易接受。这也是为什么本文以介绍Kotlin的函数及函数式编程为主。

Kotlin 函数

下面是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++函数指针的例子:

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作为一种强类型的现代化语言可以在保证稳定性的同时极大地提高开发者的开发效率。

本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/Um-Ydm8_Nd2e78c4B1G-IQ

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  237228次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8063次阅读
 目录