Kotlin之高阶函数

2023-11-17

一、定义高阶函数

高阶函数和Lambda的关系密不可分。在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()函数就是一个高阶函数。而调用一个函数类型的参数,它的语法类似于调用一个普通的函数,只需要在参数名的后面加上一对括号,并在括号中传入必要的参数即可。
高阶函数允许让函数类型的参数来决定函数的执行逻辑。即使是同一个函数高阶函数,只要传入不同的函数类型参数,那么它的执行逻辑和最终返回结果就可能是完全不同的。
举个例子:
新建一个Function.kt文件,在这个文件中编写代码。
定义一个叫作num1AndNum2()的高阶函数,并让它接收两个整型和一个函数类型的参数。我们会在num1AndNum2()函数中对传入的两个整型参数进行某种运算,并返回最终的运算结果,但是具体进行什么运算是由传入的函数类型参数决定的。

fun num1AndNum2(num1:Int,num2:Int,operator:(Int,Int)->Int):Int{
    val result=operator(num1,num2)
    return result
}

num1AndNum2()函数的前两个参数没有什么需要解释的,第三个参数是一个接收两个整型参数并且返回值也是整型的函数类型参数。在num1AndNum2()函数中,我们并没有进行任何具体的运算操作,只是将num1和num2参数传给了第三个函数类型参数,并获取它的返回值,最终将得到的返回值返回。(函数名用什么都可以,这里主要设计功能是运算,所以我命名为operator)
现在高阶函数已经定义好了,那么我们该如何调用它呢?由于num1AndNum2()函数接收一个函数类型的参数,因此我们还得先定义与其函数类型相匹配的函数才行。添加如下代码:

fun plus(num1: Int,num2: Int):Int{
return num1+num2   
}
fun minus(num1: Int,num2: Int):Int{
    return num1-num2
}

这里定义了两个函数,并且这两个函数的参数声明和返回值声明都和num1AndNum2()函数中的函数类型参数是完全匹配的。其中,plus()函数将两个参数相加并返回,minus函数将两个参数相减并返回,分别对应了两种不同的运算操作。
有了上述函数后,我们就可以调用num1AndNum2()函数了,在main()函数中编写如下代码:

fun main(){
    val num1=100
    val num2=80
    val result1= num1AndNum2(num1,num2,::plus)
    val result2 = num1AndNum2(num1, num2, ::minus)
    println("result1 is $result1")
    println("result2 is $result2")
}

这里调用num1AndNum2()函数的方式,第三个参数使用了::plus和::minus这种写法。这是一个函数引用方式的写法,表示将plus()和minus()函数作为参数传递给num1AndNum2()函数,而由于num1AndNum2()函数中使用了传入的函数类型参数来决定运算逻辑,因此这里实际上就是分别使用了plus()和minus()函数来对两个数字进行运算。
运行程序结果如下:
在这里插入图片描述
使用这种函数引用的写法虽然能够正常工作,但是如果每次调用任何高阶函数的时候都还得定义一个与其函数类型参数匹配的函数,这样有时候会比较复杂。
因此Kotlin还支持其他方式来调用高阶函数,比如Lambda表达式、匿名函数、成员引用等。其中Lambda表达式是最常见也是最普遍的高阶函数调用方式。
使用Lambda表达式的写法实现的话,代码如下:

fun main(){
val num1=100
val num2=80
val result1=num1AndNum2(num1,num2){ n1,n2 ->
n1+n2
}
val result2=num1AndNum2(num1,num2){ n1,n2 ->
n1-n2
}
println("result1 is $result1")
println("result2 is $result2")
}

使用Lambda表达式同样可以完整地表达一个函数的参数声明和返回值声明(Lambda表达式中的最后一行代码会自动作为返回值)
现在就可以将plus()和minus()函数删掉了,重新运行代码,结果也是一样的。
下面我们继续对高阶函数进行探究,我们知道apply函数,他可以用于给Lambda表达式提供一个指定的上下文,当需要连续调用同一个对象的多个方法时,apply函数可以让代码变得更加精简,比如StringBuilder就是一个典型的例子。接下来我们就使用高阶函数模仿实现一个类似的功能。
修改Function.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函数基本一样,而apply函数可以作用在所有类上面,而build函数想实现需要借助于Kotlin泛型。

二、内联函数的作用

高阶函数用途也十分广泛,可是它的实现原理是怎样的呢?我们来简单分析一下高阶函数的实现原理。
这里仍然使用刚才编写的numAndNum2()函数来举例,代码如下:

fun num1AndNum2(num1:Int,num2:Int,operator:(Int,Int)->Int):Int{
    val result=operation(num1,num2)
    return result
}
fun main(){
val num1=100
val num2=80
val result=num1AndNum2(num1,num2){n1,n2 ->
n1+n2
}
}

可以看到,上述代码中调用了num1AndNum2()函数,并通过Lambda表达式指定传入的两个整型参数进行求和。这段代码在Kotlin中好理解,因为这是高阶函数最基本的用法。我们知道Kotlin的代码最终还是要编写成Java字节码的。但是Java中并没有高阶函数的概念。
那么Kotlin是怎么让Java支持这种高阶函数的语法呢?这就要归功于Kotlin强大的编译器了。Kotlin的编译器会将这些高阶函数语法转换成Java支持的语法结构,上述代码大致会被转换成如下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(){
@Override
public 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的逻辑,并将结果返回。
这就是高阶函数背后的实现原理。会发现原来我们一直使用的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表达式所带来的运行时开销。

三、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("println end")
}
fun main(){
println("main start")
val str=""
printString(str){s->
println("lambda start")
if(s.isEmpty())
return@printString
println(s)
println("lambda end")
}
println("main end")
}

这里定义了一个叫作printlnString()的高阶函数,用于在Lambda表达式中打印传入的字符串参数。但是如果字符串参数为空,那么就不进行打印。注意,Lambda表达式中是不允许直接使用return关键字,这里使用了return@printString的写法,表示进行局部返回,并且不再执行Lambda表达式的剩余部分代码。
现在我们就刚好传入一个空的字符串参数,运行程序
打印结果如下:
在这里插入图片描述

可以看到,除了Lambda表达式中return@printString语句之后的代码没有打印,其他的日志是可以打印的,说明return@printString确实只能进行局部返回。
但是如果我们将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())
return
println(s)
println("lambda end")
}
println("main end")
}

现在printlnString()函数变成了内联函数,那么我们就可以在Lambda表达式中使用return关键字了。此时的return代表的是返回外层的调用函数,也就是main()函数(因为内联函数在编译的时候会进行代码替换的过程,替换到函数调用的地方也就是main()函数)。运行程序打印结果如下:
在这里插入图片描述

可以看到,不管是main()函数还是printString()函数,确实都是在return关键字之后停止执行了。
将高阶函数声明成内联函数是一种良好的编程习惯,事实上绝大多数高阶函数是可以直接声明成内联函数的,但是也有少部分例外的情况。比如:

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的声明,代码就可以正常编译通过了。
通过前面的分析,我们知道因为内联函数的Lambda表达式中允许使用return关键字,和高阶函数的匿名类实现不允许使用return关键字之间造成了冲突。而crossinline关键字就像一个契约,它用于保证在内联函数的Lambda表达式中一定不会使用return关键字,这样冲突就不存在了
声明了crossinline之后,我们就无法在调用runRunnable函数时的Lambda表达式中使用return关键字进行函数返回了,但是仍然可以使用return@runRunnable进行局部返回。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Kotlin之高阶函数 的相关文章

  • 在包“android”中找不到属性“backgroundTint”的资源标识符

    我发现了一些视图 xml 属性 例如backgroundTint backgroundTintMode 但是当我使用它作为视图属性定义时 Eclipse 显示错误 No resource identifier found for attri
  • React Native 从 JavaScript 代码内部访问 strings.xml

    有没有办法访问当前值android app src main res values strings xml从 JavaScript 代码内部 我想为每个构建放置不同的端点 URL 但我什至无法检测到反应本机代码内的构建类型 而不必求助于 D
  • Sqlite数据库生命周期?关闭应用程序后它会被删除吗?

    我正在遵循一个简单的教程 该教程创建一个从 SQLiteOpenHelper 扩展的类 并创建一个包含一个表和 5 行的数据库 好的 但我需要更多地了解 android Sqlite 数据库 例如 如果应用程序关闭或手机关机会发生什么 数据
  • android中向sqlite中插入大量数据

    目前 我必须一次向我的 Android 中插入超过 100 亿条数据 然而 内存不足的问题会使程序崩溃 sqlite 插入测试非常简单 只需使用 for 循环生成 sql 插入命令并通过 开始 和 提交 进行包装 private Array
  • Android - 从资产中解析巨大(超大)JSON 文件的最佳方法

    我正在尝试从资产文件夹中解析一些巨大的 JSON 文件 我如何加载并添加到 RecyclerView 我想知道解析这种大文件 大约 6MB 的最佳方法是什么 以及您是否知道可以帮助我处理此文件的良好 API 我建议您使用GSON lib h
  • 无法获取log.d或输出Robolectrict + gradle

    有没有人能够将 System out 或 Log d 跟踪从 robolectric 测试输出到 gradle 控制台 我在用Robolectric Gradle 测试插件 https github com robolectric robo
  • android xamarin 中的 reCaptcha

    我想在 Xamarin android 应用程序中实现验证码 我抓住了这个在 Android 中集成 googles reCaptcha 验证 https www c sharpcorner com article how to integ
  • Android:捕获的图像未显示在图库中(媒体扫描仪意图不起作用)

    我遇到以下问题 我正在开发一个应用程序 用户可以在其中拍照 附加到帖子中 并将图片保存到外部存储中 我希望这张照片也显示在图片库中 并且我正在使用媒体扫描仪意图 但它似乎不起作用 我在编写代码时遵循官方的Android开发人员指南 所以我不
  • 是否有 ADB 命令来检查媒体是否正在播放

    我想使用 ADB 命令检查根植于终端的外部设备中是否正在播放音频 视频 我无法找到任何 ADB 命令 如果有 我尝试过 adb shell dumpsys media player 我想要一个命令来指定视频是否正在运行 您可以使用以下命令查
  • Android MediaExtractor seek() 对 MP3 音频文件的准确性

    我在使用 Android 时无法在eek 上获得合理的准确度MediaExtractor 对于某些文件 例如this one http www archive org download emma solo librivox emma 01
  • JavaMail 只获取新邮件

    我想知道是否有一种方法可以在javamail中只获取新消息 例如 在初始加载时 获取收件箱中的所有消息并存储它们 然后 每当应用程序再次加载时 仅获取新消息 而不是再次重新加载它们 javamail 可以做到这一点吗 它是如何工作的 一些背
  • 原色(有时)变得透明

    我正在使用最新的 SDK 版本 API 21 和支持库 21 0 2 进行开发 并且在尝试实施新的材料设计指南时遇到了麻烦 材料设计说我需要有我的primary color and my accent color并将它们应用到我的应用程序上
  • 如何在PreferenceActivity中添加工具栏

    我已经使用首选项创建了应用程序设置 但我注意到 我的 PreferenceActivity 中没有工具栏 如何将工具栏添加到我的 PreferenceActivity 中 My code 我的 pref xml
  • 如何使用InputConnectionWrapper?

    我有一个EditText 现在我想获取用户对此所做的所有更改EditText并在手动将它们插入之前使用它们EditText 我不希望用户直接更改中的文本EditText 这只能由我的代码完成 例如通过使用replace or setText
  • 如何默认在 ActionOpenDocument 意图中显示“内部存储”选项

    我需要用户选择一个自定义文件类型的文件 并将其从 Windows 文件资源管理器拖到 Android 设备上 但默认情况下内部存储选项不可用 当我使用以下命令启动意图时 var libraryIntent new Intent Intent
  • Android向menuItem添加子菜单,addSubMenu()在哪里?

    我想根据我的参数以编程方式将 OptionsMenu 内的子菜单添加到 menuItem 中 我检查了android sdk中的 MenuItem 没有addSubMenu 方法 尽管你可以找到 hasSubMenu 和 getSubMen
  • Android:膨胀布局时出现 StackOverFlowError 和 InvokingTargetException

    首先 对不起我的英语 我在膨胀布局时有一个问题 我有一个自定义视图 从 LinearLayout 扩展而来 称为按钮帮助 我在名为的布局上使用该视图加载活动 我的以下代码在所有设备和模拟器上都能完美运行 但具有 QVGA 屏幕 例如 Sam
  • Firebase 添加新节点

    如何将这些节点放入用户节点中 并创建另一个节点来存储帖子 我的数据库参考 databaseReference child user getUid setValue userInformations 您需要使用以下代码 databaseRef
  • 节拍匹配算法

    我最近开始尝试创建一个移动应用程序 iOS Android 它将自动击败比赛 http en wikipedia org wiki Beatmatching http en wikipedia org wiki Beatmatching 两
  • 强制 Listview 不重复使用视图(复选框)

    我做了一个定制Listview 没有覆盖getView 方法 Listview 中的每个项目都具有以下布局 联系布局 xml

随机推荐

  • 第3章 R语言编程基础——基于R软件的传统计算(超详细)

    3 1 统计分析 多元统计分析常用的 R 包和函数 3 1 1 多元回归分析 随机误差 计量模型 案例分析 M2 的建模与预测 残差的五数 估计参数的回归值 标准差 t检验量 p value 单变量显著性检验 拟合优度和 F 检验 Resi
  • bnu1331 赈灾捐款 C语言版

    北京师范大学珠海分校 Judge Online of ACM ICPC 1331 赈灾捐款 C语言版 include
  • ubuntu18.04下的mysql创建表

    ubuntu18 04下的mysql创建表 MySQL创建数据表 错误排查 出现报错 百度的解决思路 实际操作 MySQL创建数据表 删除表 drop table h data 创建表 CREATE TABLE IF NOT EXISTS
  • 第9章、图像按钮ImageButton(从零开始学Android)

    在Android App应用中 默认的Button按钮尽管我们可以通过样式变成圆角 但有时感觉仍然不够美观 我们可以通过采用图像按钮ImageButton改善这种现状 今天我们就一起学习一下图像按钮的使用 知识点 图像按钮ImageButt
  • 【数据结构课设】 浮点数计算器

    一 简介 1 功能介绍 实数的计算 支持取对数 幂次 开方及加减乘除运算 2 模块设计 1 菜单界面 2 计算器功能简介 3 计算器功能实现 3 计算器功能实现方法 1 字符串读入用户的表达式 2 处理字符串 包括提取实数以及中缀转后缀 维
  • idea开发中git合并的代码,

    方法一 将master主分支 合并到 子分支dev上 1 当前如果在dev分支上 先提交dev分支的代码到本地 然后推送到服务器 2 然后切换分支到master主分支上 先更新master主分支的代码到本地 然后主分支就是最新代码了 3 再
  • python 安卓模拟点击_Android后台模拟点击探索(附源码)

    工作中我们需要自制一套工具 其中遇到需要模拟点击事件的需求 类似按键精灵的功能 支持后台持续运行 满足触发条件时完成点击 经过一番探索 一共整理出两种不同的方案 AccessibilityService 和 adb shell命令 读者可自
  • Matlab中的c2d函数离散化

    把传递函数离散化 dsys c2d sys ts method 传函离散 num den tfdata dsys v 离散后提取分子分母 这里面的method有好多种 zoh 零阶保持 假设控制输入在采样周期内为常值 为默认值 foh 一阶
  • 【自动化测试】——robotframework实战(一)搭建环境

    一 前提准备 python 3 9 6 pip 下载 最新版本 setuptools 下载 最新版本 二 下载robotframework框架 管理员模式打开cmd 下载RF pip install robotframework 3 1 下
  • tomcat 配置域名

    Tomcat 配置域名 在windows中 首先找到conf下面的server xml 把Connector 标签中的端口改成80 然后把添加一个Host name为域名appBase为路径 如下 Engine 标签也是 最后在C盘 win
  • Android网络请求库的使用(okhttp、retrofit、rxjava)

    Android网络请求库的使用 前置工作 okhttp的基本使用 okhttp第一个demo okhttp的异步写法 更常见 GET请求中使用okhttp拼接参数 okhttp发起POST请求 拦截器第一个demo 打印请求的时间 retr
  • java并发编程实战

    Volatile变量 valatile是java提供的一种稍弱的同步机制 用来确保将变量的更新操作通知到其他线程 当把变量声明为volatile后 编译器与运行时都会注意到这个变量是共享的 因此不会将变量上的操作与其他内存操作一起重排序 1
  • 评测 AlibabaCloud 阿里云国际版 香港轻量云服务器的性能和网络怎么样

    此次站长带来的是 AlibabaCloud 阿里云国际版 香港轻量云服务器的评测 配置为512M内存 1核 20G云硬盘 官方网站 https www alibabacloud com 国际版 https www aliyun com 国内
  • AST混淆 二进制/八进制/十六进制数值及十六进制字符串,Unicode字符串还原

    二进制 八进制 十六进制数值及十六进制字符串 Unicode字符串还原插件 const simplifyLiteral NumericLiteral node if node extra 0 obx i test node extra ra
  • uniapp解决rich-text 富文本图片过大超出问题

    问题 如图所示 图片过大超出会超出手机屏幕 解决办法
  • 第十一届蓝桥杯 ——七段码

    题目描述 小蓝要用七段码数码管来表示一种特殊的文字 上图给出了七段码数码管的一个图示 数码管中一共有 7 段可以发光的二极管 分别标记为 a b c d e f g 小蓝要选择一部分二极管 至少要有一个 发光来表达字符 在设计字符的表达时
  • python seaborn 散点图矩阵_python数据可视化之seaborn

    seaborn importmatplotlib as mplimportmatplotlib pyplot as pltimportnumpy as npimportpandas as pd 解决坐标轴刻度负号乱码 plt rcParam
  • iOS开发问题之:Xcode打包失败IPA processing failed

    打包发现失败了 提示IPA processing failed 查看日志 IDEDistribution standard log image png 发现是因为项目中使用的SDK支持i386 x86 86这个架构 猜测是iOS13强制不支
  • 代码检视/代码审查/Code Review

    目录 一 代码检视的目的 二 代码检视前的准备 三 代码检视九句箴言 四 代码检视checklist 经验检查项 五 代码检视结果度量 六 代码质量衡量指标 七 高质量代码 一 代码检视的目的 代码检视是一种用来确认方案设计和代码实现的质量
  • Kotlin之高阶函数

    一 定义高阶函数 高阶函数和Lambda的关系密不可分 在Lambda编程的基础知识 使用的一些与集合相关的函数式API用法 如map filter函数等 又比如Kotlin的标准函数 如run apply函数等 这几个函数都有一个共同的特