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

【Kotlin】内联函数

文章目录

        • 内联函数
        • noinline: 避免参数被内联
        • 非局部返回
        • 使用标签实现Lambda非局部返回
          • 为什么要设计noinline
        • crossinline
        • 具体化参数类型

Kotlin中的内联函数之所以被设计出来,主要是为了优化Kotlin支持Lambda表达式之后所带来的开销。然而,在Java中我们似乎并不需要特别关注这个问题,因为在Java 7之后,JVM引入了一种叫做 invokedynamic的技术,它会自动帮助我们做Lambda优化。但是为什么Kotlin要引入内联函数这种手动的语法呢? 这主要还是因为Kotlin要兼容Java 6。

在Kotlin中每声明一个Lambda表达式,就会在字节码中产生一个匿名类(也就是说我们一直使用的Lambda表达式在底层被转换成了匿名类的实现方式)。该匿名类包含了一个invoke方法,作为Lambda的调用方法,每次调用的时候,还会创建一个新的匿名类对象。

可想而知,Lambda语法虽然简洁,但是额外增加的开销也不少。并且,如果Lambda捕捉了某个变量,那么每次调用的时候都会创建一个新的对象,这样导致效率较低。尤其对Kotlin这门语言来说,它当今优先要实现的目标,就是在Android这个平台上提供良好的语言特性支持。Kotlin要在Android中引入Lambda语法,必须采用某种方法来优化Lambda带来的额外开销,也就是内联函数。

内联函数

Kotlin拥抱了内联函数,在C++、C#等语言中也支持这种特性。简单的来说,我们可以用inline关键字来修饰函数,这些函数就称为了内联函数。他们的函数体在编译期被嵌入每一个被调用的地方,以减少额外生成的匿名类数,以及函数执行的时间开销。所以内联函数的工作原理并不复杂,就是Kotlin编译器会将内敛函数中的代码在编译的时候自动替换到调用它的地方,这样也就不存在运行时的开销了。 。

看看Kotlin的内联函数是具体如何操作的:

fun main(args: Array<String>) {foo {println("dive into Kotlin...")}
}fun foo(block: () -> Unit) {println("before block")block()println("end block")
}

首先,我们声明了一个高阶函数foo,可以接受一个类型为() -> Unit的Lambda,然后在main函数中调用它。以下是通过字节码反编译的相关Java代码:

public static final void main(@NotNull String[] args) {Intrinsics.checkParameterIsNotNull(args, "args");foo((Function0)null.INSTANCE);	        
}public static final void foo(@NotNull Function0 block) {Intrinsics.checkParameterIsNotNull(block, "block");String var1 = "before block";System.out.println(var1);block.invoke();var1 = "end block";System.out.println(var1);
}

据我们所知,调用foo就会产生一个Function()类型的block类,然后通过invovke方法来执行,这会增加额外的生成类和调用开销。现在,我们给foo函数加上inline修饰符,如下:

inline fun foo(block: () -> Unit) {println("before block")block()println("end block")
}

再来看看相应的Java代码:

public static final void main(@NotNull String[] args) {Intrinsics.checkParameterIsNotNull(args, "args");String va1 = "before block";System.out.println(var1);// block函数体在这里开始粘贴String var2 = "dive into Kotlin...";System.out.println(var2);// block函数体在这里结束粘贴var1 = "end block";System.out.println(var1);
}public static final void foo(@NotNull Function0 block) {Intrinsics.checkParameterIsNotNull(block, "block");String var2 = "before block";System.out.println(var2);block.invoke();var2 = "end block";System.out.println(var2);
}

foo函数体代码及被调用的Lambda代码都粘贴到了相应调用的位置。试想下,如果这是一个工程中公共的方法,或者被嵌套在一个循环调用的逻辑体中,这个方法势必会被调用很多次。通过inline的语法,我们可以彻底消除这种额外调用,从而节省了开销。

内联函数典型的一个应用场景就是Kotlin的集合类。如果你看过Kotlin的集合类API文档或者源码实现就会发现,集合函数式API,如map、filter都被定义成内联函数,如:

inline fun <T, R> Array<out T>.map {transform: (T) -> R
}: List<R>inline fun <T> Array<out T>.filter {predicate: (T) -> Boolean
}: List<T>

这个很容易理解,由于这些方法都接收Lambda作为参数,同时都需要对集合元素进行遍历操作,所以把相应的实现进行内联无疑是非常适合的。

但是内联函数不是万能的,以下情况我们应避免使用内联函数:

  • 由于JVM对普通的函数已经能够根据实际情况智能地判断是否进行内联优化,所以我们并不需要对其使用Kotlin的inline语法,那只会让字节码变得更加复杂。
  • 尽量避免对具有大量函数体的函数进行内联,这样会导致过多的字节码数量。
  • 一旦一个函数被定义为内联函数,便不能获取闭包类的私有成员,除非你把他们声明为internal。
noinline: 避免参数被内联

通过上面的例子我们已经知道,如果在一个函数的开头加上inline修饰符,那么它的函数体及Lambda参数都会被内联。然而现实中的情况比较复杂,有一种可能是函数需要接受多个参数,但我们只想对其中部分Lambda参数内联,其他的则不内联,这个又该如何处理?

解决这个问题也很简单,Kotlin在引入inline的同时,也新增了noinline关键字,我们可以把它加在不想要被内联的参数开头,该参数便不会具有内联的效果:

fun main(args: Array<String>) {foo ( {println("I am inlined...")	     	}, {println("I am not inlined...")})
}inline fun foo(block1: () -> Unit, noinline block2: () -> Unit) {println("before block")block1()block2()println("end block")
}

同样的方法,再来看看反编译的Java版本:

public static final void main(@NotNull String[] args) {Intrinsics.checkParameterIsNotNull(args, "args");Function0 block2$iv = (Function0)null.INSTANCE;String var2 = "before block";System.out.println(var2);// block1 被内联了String var3 = "I am inlined...";System.out.println(var3);// block2 还是原样block2$iv.invoke();System.out.println(var2);
}
public static final void foo(@NotNull Function0 block1, @NotNull Function0 block2) {Intrinsics.checkParameterIsNotNull(block1, "block1");Intrinsics.checkParameterIsNotNull(block2, "block2");String var3 = "before block";System.out.println(var3);block1.invoke();block2.invoke();var3 = "end block";System.out.println(var3);
}

可以看出,foo函数的block2参数在带上noinline之后,反编译后的Java代码中并没有将其函数体代码在调用处进行替换。

非局部返回

Kotlin中的内联函数除了优化Lambda开销之外,还带来了其他方面的特效,典型的就是非局部返回和具体化参数类型。我们先来看下Kotlin如何支持非局部返回。

以下是我们常见的局部返回的例子:

fun main(args: Array<String>) {foo()
}
fun localReturn() {return
}
fun foo() {println("before local return")localReturn()println("after local return")return
}
// 运行结果
before local return
after local return

正如我们所熟知的,localReturn执行后,其函数体中的return只会在该函数的局部生效,所以localReturn()之后的println函数依旧生效。我们再把这个函数换成Lambda表达式的版本:

fun main(args: Array<String>) {foo { return }
}
fun foo(returning: () -> Unit) {println("before local return")returning()println("after local return")return 
}
// 运行结果
Error:(2, 11)Kotlin: 'return' is not allowed here

这时,编译器报错了,就是说在Kotlin中,正常情况下Lambda表达式不允许存在return关键字。这时候,内联函数又可以排上用场了。我们把foo进行内联后再试试看:

fun main(args: Array<String>) {foo { return }
}
inline fun foo(returning: () -> Unit) {println("before local return")returning()println("after local return")return
}
// 运行结果
before local return 

编译顺利通过了,但结果与我们的局部返回效果不同,Lambda的return执行后直接让foo函数退出了执行。如果你仔细考虑一下,可能很快就想出了原因。因为内联函数foo的函数体及参数Lambda会直接替代具体的调用。所以实际产生的代码中,retrurn相当于是直接暴露在main函数中,所以returning()之后的代码自然不会执行,这个就是所谓的非局部返回。

使用标签实现Lambda非局部返回

另外一种等效的方式,是通过标签利用@符号来实现Lambda非局部返回。同样以上的例子,我们可以在不声明inline修饰符的情况下,这么做来实现相同的效果:

fun main(args: Array<String>) {foo { return@foo }
}
fun foo(returning: () -> Unit) {println("before local return")returning()println("after local return")return
}
// 运行结果
before local return

非局部返回尤其在循环控制中显得特别有用,比如Kotlin的forEach接口,它接收的就是一个Lambda参数,由于它也是一个内联函数,所以我们可以直接在它调用的Lambda中执行return退出上一层的程序。

fun hasZeros(list: List<Int>): Boolean {list.forEach {if (it == 0) return true // 直接返回foo函数结果}return false
}
为什么要设计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()) return@printStringprintln(s)println("lambda end")}println("main end")
}

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

main start
printString begin
lambda start
printString end
main end

可以看到,除了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()) returnprintln(s)println("lambda end")}println("main end")
}

现在printString()函数变成了内联函数,我们就可以在Lambda表达式中使用return关键字了。此时的return代表的是返回外层的调用函数,也就是main()函数,如果想不通为什么的话,可以回顾一下在上一小节中学习的内联函数的代码替换过程。现在重新运行一下程序,打印结果如下:

main start
printString begin 
lambda start

可以看到,不管是main()函数还是printString()函数,确实都在return关键字之后停止执行了,和我们所预期的结果一致。
将高阶函数声明成内联函数是一种良好的编程习惯,事实上,绝大多数高阶函数是可以直接声明成内联函数的,但是也有少部分例外的情况。观察下面的代码示例:

inline fun runRunnable(block: () -> Unit) {val runnable = Runnable {block()}runnable.run()
}

这段代码在没有加上inline关键字声明的时候绝对是可以正常工作的,但是在加上inline关键字之后就会提示如下:

Image

这个错误出现的原因解释起来可能会稍微有点复杂。首先,在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关键字进行函数返回了,但是仍然可以使用return@runRunnable的写法进行局部返回。总体来说,除了在return关键字的使用上有所区别之外,crossinline保留了内联函数的其他所有特性。

crossinline

值得注意的是,非局部返回虽然在某些场合下非常有用,但可能也存在危险。因为有时候,我们内联的函数所接收的Lambda参数常常来自于上下文其他地方。为了避免带有return的Lambda参数产生破坏,我们还可以使用crossinline关键字来修饰该参数,从而杜绝此类问题的发生。就像这样子:

fun main(args: Array<String>) {foo { return }
}
inline fun foo(crossinline returning: () -> Unit) {println("before local return")returning()println("after local return")return
}
// 运行结果
Error: (2, 11) Kotlin: 'return' is not allowed here
具体化参数类型

除了非局部返回之外,内联函数还可以帮助Kotlin实现具体化参数类型。Kotlin与Java一样,由于运行时的类型擦除,我们并不能直接获取一个参数的类型。然而,由于内联函数会直接在字节码中生成相应的函数体实现,这种情况下我们反而可以获得参数的具体类型。我们可以用reified修饰符来实现这一效果。

fun main(args: Array<String>) {getType<Int>()
}
inline fun <reified T> getType() {print(T::class)
}
// 运行结果
class kotlin.Int

这个特性在Android开发中也格外有用。比如在Java中,当我们要调用startActivity时,通常需要把具体的目标视图类作为一个参数。然而,在Kotlin中,我们可以用reified来进行简化:

inline fun <refied T : Activity> Activity.startActivity() {startActivity(Intent(this, T::class.java))
}

这样,我们进行视图导航就非常容易了,如:

startActivity<DetailActivity>()

相关文章:

【Kotlin】内联函数

文章目录 内联函数noinline: 避免参数被内联非局部返回使用标签实现Lambda非局部返回为什么要设计noinline crossinline具体化参数类型 Kotlin中的内联函数之所以被设计出来&#xff0c;主要是为了优化Kotlin支持Lambda表达式之后所带来的开销。然而&#xff0c;在Java中我们似…...

Unity技美35——再URP管线环境下,配置post后期效果插件(post processing)

前两年在我的unity文章第10篇写过&#xff0c;后效滤镜的使用&#xff0c;那时候大部分项目用的还是unity的基础管线&#xff0c;stander管线。 但是现在随着unity的发展&#xff0c;大部分项目都用了URO管线&#xff0c;甚至很多PC端用的都是高效果的HDRP管线&#xff0c;这就…...

Redis:持久化RDB和AOF

目录 概述RDB持久化流程指定备份文件的名称指定备份文件存放的目录触发RDB备份redis.conf 其他一些配置rdb的备份和恢复优缺点停止RDB AOF持久化流程AOF启动/修复/恢复AOF同步频率设置rewrite压缩原理触发机制重写流程no-appendfsync-on-rewrite 优缺点 如何选择 概述 Redis是…...

基于python协同过滤推荐算法的音乐推荐与管理系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 基于Python的协同过滤推荐算法的音乐推荐与管理系统是一个集成了音乐推荐和管理的系统&#xff0c;它使用协同过滤算…...

【极客技术】真假GPT-4?微调 Llama 2 以替代 GPT-3.5/4 已然可行!

近日小编在使用最新版GPT-4-Turbo模型&#xff08;主要特点是支持128k输入和知识库截止日期是2023年4月&#xff09;时&#xff0c;发现不同商家提供的模型回复出现不一致的情况&#xff0c;尤其是模型均承认自己知识库达到2023年4月&#xff0c;但当我们细问时&#xff0c;Fak…...

STK Components 二次开发-创建地面站

1.地面站只需要知道地面站的经纬高。 // Define the location of the facility using cartographic coordinates.var location new Cartographic(Trig.DegreesToRadians(-75.596766667), Trig.DegreesToRadians(40.0388333333), 0.0); 2.创建地面站 创建方式和卫星一样生成对…...

数据结构与算法(三)贪心算法(Java)

目录 一、简介1.1 定义1.2 基本步骤1.3 优缺点 二、经典示例2.1 选择排序2.2 背包问题 三、经典反例&#xff1a;找零钱3.1 题目3.2 解答3.3 记忆化搜索实现3.4 动态规划实现 一、简介 1.1 定义 贪心算法&#xff08;Greedy Algorithm&#xff09;&#xff0c;又名贪婪法&…...

057-第三代软件开发-文件监视器

第三代软件开发-文件监视器 文章目录 第三代软件开发-文件监视器项目介绍文件监视器实现原理关于 QFileSystemWatcher实现代码 关键字&#xff1a; Qt、 Qml、 关键字3、 关键字4、 关键字5 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QML&…...

二十七、微服务案例

目录 一、实现输入搜索功能 1、下载代码&#xff0c;在idea上打开 2、新建RequestParams类&#xff0c;用于接收解析请求 3、在启动类中加入客户端地址Bean&#xff0c;以便实现服务 4、编写搜索方法 5、新建返回分页结果类 6、实现搜索方法 7、编写控制类&#xff0c;…...

(C++)string类的模拟实现

愿所有美好如期而遇 前言 我们模拟实现string类不是为了去实现他&#xff0c;而是为了了解他内部成员函数的一些运行原理和时间复杂度&#xff0c;在将来我们使用时能够合理地去使用他们。 为了避免我们模拟实现的string类与全局上的string类冲突(string类也在std命名空间中)&…...

处理数据中的缺失值--删除缺少值的行

两个最主要的处理缺失值的方法是&#xff1a; ❏ 删除缺少值的行&#xff1b; ❏ 填充缺失值&#xff1b; 我们首先将serum_insulin的中的字段值0替换为None&#xff0c;可以看到缺失值的数量为374个&#xff1b; print(pima[serum_insulin].isnull().sum()) pima[serum_insu…...

Kotlin学习——kt里的集合,Map的各种方法之String篇

Kotlin 是一门现代但已成熟的编程语言&#xff0c;旨在让开发人员更幸福快乐。 它简洁、安全、可与 Java 及其他语言互操作&#xff0c;并提供了多种方式在多个平台间复用代码&#xff0c;以实现高效编程。 https://play.kotlinlang.org/byExample/01_introduction/02_Functio…...

MIT 6.824 -- MapReduce Lab

MIT 6.824 -- MapReduce Lab 环境准备实验背景实验要求测试说明流程说明 实验实现GoLand 配置代码实现对象介绍协调器启动工作线程启动Map阶段分配任务执行任务 Reduce 阶段分配任务执行任务 终止阶段 崩溃恢复 注意事项并发安全文件转换golang 知识点 测试 环境准备 从官方gi…...

创新研报|顺应全球数字化,能源企业以“双碳”为目标的转型迫在眉睫

能源行业现状及痛点分析 挑战一&#xff1a;数字感知能力较弱 挑战二&#xff1a;与业务的融合度低 挑战三&#xff1a;决策响应速度滞后 挑战四&#xff1a;价值创造有待提升 挑战五&#xff1a;安全风险如影随形 能源数字化转型定义及架构 能源行业数字化转型体系大体…...

Blender 连续 5 天遭受大规模 DDoS 攻击

Blender 发布公告指出&#xff0c;在2023年11月18日至23日期间&#xff0c;blender.org 网站遭受了持续的分布式拒绝服务&#xff08;DDoS&#xff09;攻击&#xff0c;攻击者通过不断发送请求导致服务器超载&#xff0c;使网站运营严重中断。此次攻击涉及数百个 IP 地址的僵尸…...

Python 获取本地和广域网 IP

Python 获取本地IP &#xff0c;使用第三方库&#xff0c;比如 netifaces import netifaces as nidef get_ip_address():try:# 获取默认网络接口&#xff08;通常是 eth0 或 en0&#xff09;default_interface ni.gateways()[default][ni.AF_INET][1]# 获取指定网络接口的IP地…...

静态路由配置过程

静态路由 静态路由简介 路由器在转发数据时&#xff0c;要先在路由表&#xff08;Routing Table&#xff09;中在找相应的路由&#xff0c;才能知道数据包应该从哪个端口转发出去。路由器建立路由表基本上有以下三种途径。 &#xff08;1&#xff09;直连路由&#xff1a;路由…...

基于OGG实现MySQL实时同步

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…...

【计算机网络笔记】多路访问控制(MAC)协议——轮转访问MAC协议

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…...

什么是好的FPGA编码风格?(3)--尽量不要使用锁存器Latch

前言 在FPGA设计中&#xff0c;几乎没人会主动使用锁存器Latch&#xff0c;但有时候不知不觉中你的设计莫名其妙地就生成了一堆Latch&#xff0c;而这些Latch可能会给你带来巨大的麻烦。 什么是锁存器Latch&#xff1f; Latch&#xff0c;锁存器&#xff0c;一种可以存储电路…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

2025季度云服务器排行榜

在全球云服务器市场&#xff0c;各厂商的排名和地位并非一成不变&#xff0c;而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势&#xff0c;对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析&#xff1a; 一、全球“三巨头”…...