即使未在函数体中调用infix参数也要计算

问题描述

我正试图利用

uproot.rootio.TH2D

但它会引发Arrayindexoutofboundsexception

update tTable
set text_modified=replace(text_nvarchar,'bonus_score',bonus_score);

因为

infix fun <T> Boolean.then(param: T): T? = if (this) param else null

我如何使其在Kotlin中工作?

解决方法

在Kotlin中,会急切地评估函数参数:调用函数时,会在将控制权传递给函数之前计算出每个参数的值。无论是否在函数中使用该值,都会发生这种情况。 (毕竟,通常在不实际运行代码的情况下,您不能告诉是否使用它。 *

(对于infix函数和标准调用来说都是正确的;尽管语法不同,但含义完全相同。)

事实上,对于大多数其他运算符也是如此:当您添加两个数字,连接两个字符串,从函数返回值或其他任何操作时,将首先对每个操作数求值。仅有少数例外,其中短路&&||运算符最为明显。

因此,在您的情况下,诸如then data[index - 1].id之类的调用将始终首先评估data[index - 1].id,然后再将其传递给then()函数;因此,如果index为0,则将抛出ArrayIndexOutOfBoundsException。

如果您希望对其进行评估,则必须传递一个lambda,例如:

infix fun <T> Boolean.then(lazyValue: () -> T): T?
        = if (this) lazyValue() else null

然后您可以像这样使用它:

(index > 0) then { data[index - 1].id }

发生的事情是对代码进行了评估,给出了一个lambda,并将其作为lazyValue传递给函数;但是除非/ until * 到达函数中的lazyValue(),否则不评估lambda的内容

您可以在诸如require(value,lazyMessage)之类的库函数中看到此模式;由于几乎总是可以满足该要求,并且消息通常是在运行时必须构造的复杂字符串,因此仅在条件为false时,才通过评估其第二个参数来避免创建不必要的String对象。

传递lambda的不利之处在于它的效率略低:它需要创建一个对象来表示lambda,从而增加了一点额外的CPU和堆。 (视情况而定,它可能每次都需要创建一个新对象,或者可能能够重用同一对象。)

但是Kotlin提供了一种解决方法:如果将函数标记为inline,则它既避免了函数调用又避免了lambda,并有效地将代码直接“粘贴”到函数的内联副本中,就像您用手“写出”函数体一样高效。


(**有一个名为contracts的实验性功能,可让您告诉编译器您是否确定在什么情况下会评估lambda。 避免某些类型的警告或错误-尽管它不会改变评估顺序,所以您在这里仍然需要一个lambda。)