Kotlin 动态扩展循环上边框
我面临以下问题。下面是我的方法,它替换字符串中从abs value到Math.abs(如| 7 |->到Math.abs(7))的所有垂直线:Kotlin 动态扩展循环上边框,kotlin,Kotlin,我面临以下问题。下面是我的方法,它替换字符串中从abs value到Math.abs(如| 7 |->到Math.abs(7))的所有垂直线: 我需要执行它的次数与新的表达式字符串长度相同。但当我运行应用程序时,传递参数“| 7 |”的循环只执行了3次。问题出在哪里?我如何解决它?正如Michael Butscher所说,for()循环的循环条件在开始时计算一次,而不是在循环中每次都计算一次。因此,在您的示例中,字符串以3个字符开始,因此它是循环的 Kotlin讨论板对此进行了讨论 一个直接的解
我需要执行它的次数与新的
表达式字符串长度相同。但当我运行应用程序时,传递参数“| 7 |”的循环只执行了3次。问题出在哪里?我如何解决它?正如Michael Butscher所说,for()
循环的循环条件在开始时计算一次,而不是在循环中每次都计算一次。因此,在您的示例中,字符串以3个字符开始,因此它是循环的
Kotlin讨论板对此进行了讨论
一个直接的解决方法可能是使用while()
循环,它每次都会评估条件
但是,这仍然是非常低效的。一般来说,在循环中操作字符串是一个坏主意:在这里,每次都会为子字符串创建两个新的字符串对象,一个StringBuilder收集所有内容,然后为结果创建另一个字符串。如果字符串变长,则可能会出现问题,和/或你每秒要做上千次
通常,字符串操作的答案是显式使用StringBuilder,并在将数据转换回字符串(如果有的话)之前尽可能长地保留数据。因此,您可以创建一个StringBuilder,将其初始化为字符串参数,然后循环(使用while()
loop)。您可以使用replace()
或insert()
来进行更改。但这仍然是低效的,因为它每次都必须向上移动字符串的其余部分
因此,更好的解决方案是循环原始字符串的字符,并将其复制到StringBuilder。我可以这样编写:
private fun String.replaceAbs()
= buildString {
var inAbsBracket = false
for (c in this@replaceAbs) {
if (c == '|') {
inAbsBracket = !inAbsBracket
append(if (inAbsBracket) "Math.abs(" else ")")
} else
append(c)
}
}
我没有使用参数,而是将其作为扩展函数(String.replaceAbs()
),因此您可以这样调用它:“|7 |”。.replaceAbs()
(当然,它也可以作为普通函数使用,但我发现这种扩展函数读起来很好。)
这使用了一个StringBuilder
,但不是显式创建它,而是使用标准库的函数(它为您创建了一个函数),允许您在lambda中作为This
访问它,然后将其转换为字符串。由于所有函数体都在lambda中,给函数一个表达式体(而不是{
..}
)稍微简洁一些
我翻转了条件,称它为inAbsBracket
,因此它以false开头,当我们在括号内时设置为true;这似乎更容易理解
它在原始字符串中的每个字符上循环。(这里您必须调用它this@replaceAbs
,因为这个引用了buildString()
lambda()中的StringBuilder。)这样做时,您不需要知道索引,所以直接循环字符更简单
然后它附加Math.abs(
或)
来代替|
,或者附加原始字符串中的字符;它需要执行后一种操作,因为它正在创建原始字符串的副本,而不是就地操作它。(因为这个是lambda中的StringBuilder,它可以直接调用append()
)
这是你能得到的最有效的方法;它只对字符串进行一次扫描,并且只为结果创建一个StringBuilder和一个字符串。希望它也相对容易阅读-至少,一旦你习惯了Kotlin的一些很酷的功能 最初输入for循环时,“until”操作符创建一个range对象,该对象在“expression.length”更改后不会调整其结尾。尝试一下while循环。这是我在这里得到的最好答案,非常感谢@谢尔盖·哈洛夫斯基,我的荣幸! 几乎任何不平凡的代码都可以得到改进(毫无疑问,包括我上面写的代码!),这是一个很好的练习,可以让您了解如何使代码变得更简单、更短、更易于阅读、更高效、更健壮、更易用和/或更通用。 有时这些事情会发生冲突,所以你必须平衡它们;但当他们一起工作的时候,感觉很棒。 (当然,有些东西是品味的问题。) 如果我给了你一些想法,那就不客气了!
private fun String.replaceAbs()
= buildString {
var inAbsBracket = false
for (c in this@replaceAbs) {
if (c == '|') {
inAbsBracket = !inAbsBracket
append(if (inAbsBracket) "Math.abs(" else ")")
} else
append(c)
}
}