是否可以在 Kotlin 中编写一个 for 循环,以便在迭代期间将索引更改为我想要的任何值?
我知道我可以写为一个这样的循环:
for (int i = 0; i < n; i++) {
...
if (condition) i = <any valid index>
}
但是我不能做我想做的事。我想写一个像java一样的for循环
i
但能够将 i
的值更改为我想要的任何值。有很多时候需要。
我目前的用例是检查标签是否有效。我使用 for 循环遍历文本。当当前字符等于“<”时,我找到下一个“>”的索引并检查它们之间的文本是否是有效的标签名称。如果是这样,我将 foreach
的值设置为 ">" 的索引。我找不到在 Kotlin 中使用 for 循环的方法,只能使用 while 循环。如果使用while循环,需要在外面声明索引,在循环内部处理增量,有点烦人,容易出错。
答案 0 :(得分:1)
如您所见,…in…
for
循环的索引在 Kotlin 中是只读的;你不能在循环内改变它。
主要有两种方法。首先,在某些情况下,您可能能够在迭代本身中包含跳转——也许迭代某个过程的结果,而不是一个简单的范围。这样可以使循环更清晰、更简洁;但它只适用于某些特定情况。
否则,恐怕一般情况只是像您建议的那样使用 while
循环:
var i = 1
while (i <= 3) {
println(i)
++i
}
i = 6
while (i >= 0) {
println(i)
i -= 2
}
正如你所说,这当然更尴尬:初始值、增量/减量和最终值现在在三个不同的地方,因此循环更难阅读。并且循环变量不再被限制在循环本身的范围内,这会污染封闭函数/块的命名空间并在以后有混淆的风险(特别是如果变量被重用,如上所述)。
但它更通用:循环变量每次通过循环都可以调整任意量,即使是以事先未知的方式。
不幸的是,没有简单的方法可以将其包装成通用的高阶函数(这就是 Kotlin 允许伪造许多其他语言结构的方式^H^H^H^H^H^Himplemented)。这是出于同样的原因:lambda 参数(无论是调用 it
还是显式命名)都是只读的。
并预测您的下一个问题:我认为没有关于为什么 for
索引(如函数参数)是只读的官方说法,但我怀疑这是因为让它们可变会导致混淆和其他错误。 (如果您的 Java 示例又长又复杂,则可能很容易错过循环内对 i
的更新,并且从循环的顶部假设它每次只增加 1。 或者可能有可能会意外更新循环计数器,从而为不同的变量输入错误。)
答案 1 :(得分:0)
就像函数参数一样,for循环的变量也是只读的。如果不想使用 while 循环,则需要创建本地 var
才能对其进行修改。
for (x in 0..n) {
var i = x
//...
}
或者,对于您提到的用例可能更清楚:
for (x in 0..n) {
val i = if (condition) x + 1 else x
//...
}
如果您想知道为什么存在此限制,那是因为它被认为是允许它的语言中错误的常见来源。
答案 2 :(得分:0)
For 循环可以与实现 Iterable
/Iterator
接口的任何对象一起使用,因此您可以执行以下技巧:
next()
调用时返回不是一个简单的 Int
,而是一个包含指向迭代器本身的链接的对象IntProgression
检索此迭代器interface Resettable {
fun resetTo(x: Int)
}
class ResettableIterator(p: IntProgression) : Iterator<ResettableInt>, Resettable {
private val last: Int = p.last
private val step: Int = p.step
private var delegate: IntIterator = p.iterator()
override fun hasNext(): Boolean = delegate.hasNext()
override fun next() = ResettableInt(delegate.nextInt(), this)
override fun resetTo(x: Int) {
delegate = IntProgression.fromClosedRange(x, last, step).iterator()
}
}
data class ResettableInt(val value: Int, private val iterator: ResettableIterator) : Resettable by iterator
fun IntProgression.resettable() = ResettableIterator(this)
用法:
for (i in (1..10 step 2).resettable()) {
if (condition(i.value)) i.resetTo(<any valid index>)
}
但是,我不建议使用这种方法,因为它不常见,因此相当混乱。
在你的情况下,我不明白你为什么需要使用低级 for-each char 循环 - regex one-liner 更适合于此:"<(.+?)>".toRegex().findAll(text).map { it.groupValues[1] }.all { isValid(it) }