当前位置: 首页 > news >正文

dedecms网站后台管理系统网游百度搜索风云榜

dedecms网站后台管理系统,网游百度搜索风云榜,爱做网站视频,是不是做推广都得有网站1. 高阶函数 1.1 定义高阶函数 高阶函数和Lambda的关系是密不可分的。一些与集合相关的函数式API的用法#xff0c;如map、filter函数等,Kotlin的标准函数#xff0c;如run、apply函数等。这几个函数有一个共同的特点#xff1a;它们都会要求我们传入一个Lambda表达式作为参…1. 高阶函数 1.1 定义高阶函数 高阶函数和Lambda的关系是密不可分的。一些与集合相关的函数式API的用法如map、filter函数等,Kotlin的标准函数如run、apply函数等。这几个函数有一个共同的特点它们都会要求我们传入一个Lambda表达式作为参数。像这种接收Lambda参数的函数就可以称为具有函数式编程风格的API而如果你想要定义自己的函数式API那就得借助高阶函数来实现了. 如果一个函数接收另一个函数作为参数或者返回值的类型是另一个函数那么该函数就称为高阶函数。 一个函数怎么能接收另一个函数作为参数呢这就涉及另外一个概念了函数类型。我们知道编程语言中有整型、布尔型等字段类型而Kotlin又增加了一个函数类型的概念。如果我们将这种函数类型添加到一个函数的参数声明或者返回值声明当中那么这就是一个高阶函数了。 接下来我们就学习一下如何定义一个函数类型。不同于定义一个普通的字段类型函数类型的语法规则是有点特殊的基本规则如下 (String, Int) - Unit既然是定义一个函数类型那么最关键的就是要声明该函数接收什么参数以及它的返回值是什么。因此-左边的部分就是用来声明该函数接收什么参数的多个参数之间使用逗号隔开如果不接收任何参数写一对空括号就可以了。而-右边的部分用于声明该函数的返回值是什么类型如果没有返回值就使用Unit它大致相当于Java中的void。 现在将上述函数类型添加到某个函数的参数声明或者返回值声明上那么这个函数就是一个高 阶函数了如下所示 fun example(func: (String, Int) - Unit) {func(hello, 123) }可以看到这里的example()函数接收了一个函数类型的参数因此example()函数就是一个高阶函数。而调用一个函数类型的参数它的语法类似于调用一个普通的函数只需要在参数名的后面加上一对括号并在括号中传入必要的参数即可。 高阶函数允许让函数类型的参数来决定函数的执行逻辑。即使是同一个高阶函数只要传入不同的函数类型参数那么它的执行逻辑和最终的返回结果就可能是完全不同的。类似于Java中的回调函数,同样的参数,由于回调函数的实现不同,那么结果也是完全不同的,Kotlin中的高阶函数把设置回调函数和调用回调函数放在了一起. 1.2 高阶函数的使用 如果每次调用任何高阶函数的时候都还得先定义一个与其函数类型参数相匹配的函数这是不是有些太复杂了因此Kotlin还支持其他多种方式来调用高阶函数比如Lambda表达式、匿名函数、成员引用等。 回顾之前学习的apply函数它可以用于给Lambda表达式提供一个指定的上下文当需要连续调用同一个对象的多个方法时apply函数可以让代码变得更加精简比如StringBuilder就是一个典型的例子。接下来我们就使用高阶函数模仿实现一个类似的功能。修改HigherOrderFunction.kt文件在其中加入如下代码 fun StringBuilder.build(block: StringBuilder.() - Unit): StringBuilder {block()return this }这里我们给StringBuilder类定义了一个build扩展函数这个扩展函数接收一个函数类型参 数并且返回值类型也是StringBuilder。 注意这个函数类型参数的声明方式和我们前面学习的语法有所不同它在函数类型的前面加上了一个StringBuilder. 的语法结构。这是什么意思呢其实这才是定义高阶函数完整的语法规则函数类型的前面加上ClassName. 就表示这个函数类型是定义在哪个类当中的。那么这里将函数类型定义到StringBuilder类当中有什么好处呢好处就是当我们调用build函数时传入的Lambda表达式将会自动拥有StringBuilder的上下文同时这也是apply函数的实现方式。 现在我们就可以使用自己创建的build函数来简化StringBuilder构建字符串的方式了。 fun main() {val list listOf(Apple, Banana, Orange, Pear, Grape)val result StringBuilder().build {append(Start eating fruits.\n)for (fruit in list) {append(fruit).append(\n)}append(Ate all fruits.)}println(result.toString()) }可以看到build函数的用法和apply函数基本上是一模一样的只不过我们编写的build函数目前只能作用在StringBuilder类上面而apply函数是可以作用在所有类上面的。如果想实现apply函数的这个功能需要借助于Kotlin的泛型才行. 1.3 高阶函数的原理及内联函数 我们还是简单分析一下高阶函数的实现原理,使用刚num1AndNum2()函数来举例代码如下所示 fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) - Int): Int {val result operation(num1, num2)return result } fun main() {val num1 100val num2 80val result num1AndNum2(num1, num2) { n1, n2 -n1 n2} }可以看到上述代码中调用了num1AndNum2()函数并通过Lambda表达式指定对传入的两个整型参数进行求和。这段代码在Kotlin中非常好理解因为这是高阶函数最基本的用法。可是我们知道Kotlin的代码最终还是要编译成Java字节码的但Java中并没有高阶函数的概念。 那么Kotlin究竟使用了什么魔法来让Java支持这种高阶函数的语法呢这就要归功于Kotlin强大 的编译器了。Kotlin的编译器会将这些高阶函数的语法转换成Java支持的语法结构上述的Kotlin代码大致会被转换成如下Java代码 public static int num1AndNum2(int num1, int num2, Function operation) {int result (int) operation.invoke(num1, num2);return result; } public static void main() {int num1 100;int num2 80;int result num1AndNum2(num1, num2, new Function() {Overridepublic Integer invoke(Integer n1, Integer n2) {return n1 n2;}}); }考虑到可读性我对这段代码进行了些许调整并不是严格对应了Kotlin转换成的Java代码。可 以看到在这里num1AndNum2()函数的第三个参数变成了一个Function接口这是一种Kotlin内置的接口里面有一个待实现的invoke()函数。而num1AndNum2()函数其实就是调用了Function接口的invoke()函数并把num1和num2参数传了进去。在调用num1AndNum2()函数的时候之前的Lambda表达式在这里变成了Function接口的匿名类实现然后在invoke()函数中实现了n1 n2的逻辑并将结果返回。 这就是Kotlin高阶函数背后的实现原理。你会发现原来我们一直使用的Lambda表达式在底层 被转换成了匿名类的实现方式。这就表明我们每调用一次Lambda表达式都会创建一个新的匿名类实例当然也会造成额外的内存和性能开销。为了解决这个问题Kotlin提供了内联函数的功能它可以将使用Lambda表达式带来的运行时开销完全消除。 内联函数的用法非常简单只需要在定义高阶函数时加上inline关键字的声明即可如下所示 inline fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) - Int): Int {val result operation(num1, num2)return result }内联函数的工作原理又是什么呢其实并不复杂就是Kotlin编译器会将内联函数中的代码在编译的时候自动替换到调用它的地方这样也就不存在运行时的开销了。 我们通过图例的方式来详细说明内联函数的代码替换过程。首先Kotlin编译器会将Lambda表达式中的代码替换到函数类型参数调用的地方:  接下来再将内联函数中的全部代码替换到函数调用的地方  最终的代码就被替换成了如下图所示的样子  正是如此内联函数才能完全消除Lambda表达式所带来的运行时开销。在编译时,将Lambda表达式中的实现代码替换到内联函数中,从而消除了额外的资源开销. 1.4 noinline与crossinline 接下来我们要讨论一些更加特殊的情况。比如一个高阶函数中如果接收了两个或者更多函数类型的参数这时我们给函数加上了inline关键字那么Kotlin编译器会自动将所有引用的Lambda表达式全部进行内联。但是如果我们只想内联其中的一个Lambda表达式该怎么办呢这时就可以使用noinline关键字了如下所示 inline fun inlineTest(block1: () - Unit, noinline block2: () - Unit) { }可以看到这里使用inline关键字声明了inlineTest()函数原本block1和block2这两 个函数类型参数所引用的Lambda表达式都会被内联。但是我们在block2参数的前面又加上了 一个noinline关键字那么现在就只会对block1参数所引用的Lambda表达式进行内联了。这就是noinline关键字的作用。 前面我们已经解释了内联函数的好处那么为什么Kotlin还要提供一个noinline关键字来排除内联功能呢这是因为内联的函数类型参数在编译的时候会被进行代码替换因此它没有真正的参数属性。非内联的函数类型参数可以自由地传递给其他任何函数因为它就是一个真实的参数而内联的函数类型参数只允许传递给另外一个内联函数这也是它最大的局限性。 另外内联函数和非内联函数还有一个重要的区别那就是内联函数所引用的Lambda表达式 中是可以使用return关键字来进行函数返回的而非内联函数只能进行局部返回, 这一点思考一下内联函数编译替换的过程即可理解。为了说明这个问题我们来看下面的例子。 fun printString(str: String, block: (String) - Unit) {println(printString begin)block(str)println(printString end) } fun main() {println(main start)val str printString(str) { s -println(lambda start)if (s.isEmpty()) returnprintStringprintln(s)println(lambda end)}println(main end) }这里定义了一个叫作printString()的高阶函数用于在Lambda表达式中打印传入的字符串参数。但是如果字符串参数为空那么就不进行打印。注意Lambda表达式中是不允许直接使用return关键字的这里使用了returnprintString的写法表示进行局部返回并且不再执行Lambda表达式的剩余部分代码。现在我们就刚好传入一个空的字符串参数运行程序打印结果如图所示。 可以看到除了Lambda表达式中returnprintString语句之后的代码没有打印其他的日志是正常打印的说明returnprintString确实只能进行局部返回。但是如果我们将printString()函数声明成一个内联函数那么情况就不一样了如下所示 inline fun printString(str: String, block: (String) - Unit) {println(printString begin)block(str)println(printString end) } fun main() {println(main start)val str printString(str) { s -println(lambda start)if (s.isEmpty()) returnprintln(s)println(lambda end)}println(main end) }现在printString()函数变成了内联函数我们就可以在Lambda表达式中使用return关键字了。此时的return代表的是返回外层的调用函数也就是main()函数如果想不通为什么的话可以回顾一下在上一小节中学习的内联函数的代码替换过程(return语句直接替换到main()函数中了,终止了main()函数的允许)。现在重新运行一下程序打印结果如图6.17所示。 将高阶函数声明成内联函数是一种良好的编程习惯事实上绝大多数高阶函数是可以直接声 明成内联函数的但是也有少部分例外的情况。观察下面的代码示例 inline fun runRunnable(block: () - Unit) {val runnable Runnable {block()}runnable.run() }这段代码在没有加上inline关键字声明的时候绝对是可以正常工作的但是在加上inline关 键字之后就会提示如图所示的错误。  这个错误出现的原因解释起来可能会稍微有点复杂。首先在runRunnable()函数中我们创建了一个Runnable对象并在Runnable的Lambda表达式中调用了传入的函数类型参数。而Lambda表达式在编译的时候会被转换成匿名类的实现方式也就是说上述代码实际上是在匿名类中调用了传入的函数类型参数。而内联函数所引用的Lambda表达式允许使用return关键字进行函数返回但是由于我们是在匿名类中调用的函数类型参数此时是不可能进行外层调用函数返回的最多只能对匿名类中的函数调用进行返回因此这里就提示了上述错误。 也就是说如果我们在高阶函数中创建了另外的Lambda或者匿名类的实现并且在这些实现中调用函数类型参数此时再将高阶函数声明成内联函数就一定会提示错误。那么是不是在这种情况下就真的无法使用内联函数了呢也不是比如借助crossinline关键字就可以很好地解决这个问题 inline fun runRunnable(crossinline block: () - Unit) {val runnable Runnable {block()}runnable.run() }可以看到这里在函数类型参数的前面加上了crossinline的声明代码就可以正常编译通过了。 那么这个crossinline关键字又是什么呢前面我们已经分析过之所以会提示错误就是因为内联函数的Lambda表达式中允许使用return关键字和高阶函数的匿名类实现中不允许使用return关键字之间造成了冲突。而crossinline关键字就像一个契约它用于保证在内联函数的Lambda表达式中一定不会使用return关键字这样冲突就不存在了问题也就巧妙地解决了。 声明了crossinline之后我们就无法在调用runRunnable函数时的Lambda表达式中使用return关键字进行函数返回了但是仍然可以使用returnrunRunnable的写法进行局部返回。总体来说除了在return关键字的使用上有所区别之外crossinline保留了内联函数的其他所有特性。 1.5 总结 高阶函数就是参数中有函数类型参数的函数. 在调用时由于会产生额外的接口创建资源消耗,为了避免此缺点,引入inline内联函数的概念, 通过在编译时进行代码替换来避免额外的资源消耗. 内联函数中的函数类型参数不能像普通参数那样随意传递到其他函数中(因为编译时进行了代码替换,实际中并不存在此函数类型参数了),所以为了解决此缺点,引入noinline关键字,表示对内联函数中的函数类型参数并不进行代码替换. crossinline关键字是为了解决在高阶内联函数中,创建了另外的Lambda或者匿名类的实现并且在这些实现中调用函数类型参数导致的错误(主要是内联函数进行代码替换导致的return关键字的使用问题).
http://www.zqtcl.cn/news/615864/

相关文章:

  • 网站每天点击量多少好精选聊城做网站的公司
  • 网站建设课程基础兰州网站seo费用
  • 天助可以搜索别人网站曲靖网站推广
  • 易语言编程可以做网站么网站备案流程
  • 我想接加工单seo搜索引擎优化工资
  • 西宁做网站君博推荐wordpress如何管理
  • 个人建一个网站多少钱怎样优化网络速度
  • 网站建设项目进度表长春百度seo代理
  • 购物网站排名哪家好免费做房产网站
  • 手机免费建设网站制作南通网站建设排名公司哪家好
  • 做商城网站哪里买企业官网招聘
  • 网站自己做流量互联网营销培训平台
  • 如何查看网站备案官方网站建设状况
  • 做什麽网站有前景软件 开发 公司
  • 淘宝做短视频网站好建设银行代发工资网站
  • 北京建商城网站网站做指向是什么意思
  • 定制网站开发介绍图移动网站适配
  • 青海网站建设怎么建设腾云建站官网
  • 怎样自己做企业的网站gif制作软件app
  • 阿里云建站后台网站建设多少钱合适
  • 自媒体图片素材网站景区网站怎么做的
  • 模块化网站建设江宁做网站
  • 电视网站后台管理系统漏洞淘客推广怎么做
  • 网站建设基础大纲文案丽江网站建设 莱芜
  • 程序员找工作的网站怎么给搞笑网站做文案
  • 网站flsh怎么做能被百度收录的建站网站
  • 娄底网站seo建平台网站费用
  • seo优化网站的注意事项WordPress伪静态公告404
  • 手机网站自动适应沈阳网站建设公司电话
  • 备案号网站下边苏州广告公司招聘