Kotlin - 在索引范围内的 IntArray 中查找最小值
Kotlin - Find minimum value in an IntArray within range of indices
我正在将一些较旧的 Java 代码重构到 Kotlin。有一个函数 returns Kotlin IntArray
中的元素在 [a
, b
] 范围内保持的最小值的索引。范围值默认为 0,数组大小为 - 1。
我想做一些类似...
return data.minOf().indexOf()
...但仅在 data
的 a
和 b
索引之间迭代。
函数如下:
// data is the IntArray property that I'm looping through.
fun absMinIndex(a: Int = 0, b: Int = (data.size - 1)) : Int {
var minVal = data[a]
var minIndex = 0
for (i in (a + 1)..b) {
val e = data[i]
if (e < minVal) {
minVal = e
minIndex = i
}
}
return maxIndex
}
这个 [for 循环] 通过从不访问超出范围的索引并且不生成副本 array/sub-array 很好地解决了这个问题。我想知道是否可以完成 'prettier'.
问题
是否有更惯用的 Kotlin 方法来迭代范围内的数组,而不会对我当前解决方案的性能产生负面影响?
为清楚起见编辑了一些代码。
正如 Tenfour04 所建议的,在不损失性能的情况下,您无能为力。但是按照你的想法,改变你的调用方式,你可以把它变成一个扩展函数。
fun IntArray.findIndexOfMinInRange(a: Int = 0, b: Int = this.size - 1): Int {
var maxVal = get(a)
var maxIndex = 0
for (i in (a + 1)..b) {
if (get(i) < maxVal) {
maxVal = get(i)
maxIndex = i
}
}
return maxIndex
}
//and call it like this:
data.findIndexOfMinInRange(0, 15) //or without anything in the parentheses for the default values
我要更改的一件事是函数内的变量名,我们搜索的是最小值,而不是最大值,还有最小值的索引,而不是最大索引。也许(也许很大)创建一个 data[it]
的 val 而不是访问它两次可能会更好(老实说不确定,我们会用 .get
换取几个字节的内存)。
所以总而言之,我可能会把它留在这里:
fun IntArray.findIndexOfMinInRange(fromIndex: Int = 0, toIndex: Int = this.size - 1): Int {
var min = get(fromIndex)
var indexOfMin = fromIndex
for(i in (fromIndex + 1)..toIndex){
val current = get(i)
if (current < min) {
min = current
indexOfMin = i
}
}
return indexOfMin
}
//would be called in the same way the one above
另外,请注意,如果您创建了一个特定大小的 IntArray
,并且没有完全填充它,它将为未填充的默认值保留 0
.如果你这样做:
val data = IntArray(6)
data[0] = 10
data[1] = 11
data[2] = 100
data[3] = 9
data[4] = 50
那么实际的数组就是[10, 11, 100, 9, 50, 0]
.
我相信这种方法会更地道:
- 使用
IntRange
作为输入参数
- 为
IntArray
定义扩展方法,提供自定义迭代器以遍历所需范围内的列表,将值包装到 IndexedValue
:
fun IntArray.withIndexInRange(range: IntRange = 0..lastIndex) = Iterable {
require(range.first >= 0 && range.last <= lastIndex)
object : Iterator<IndexedValue<Int>> {
private var index = range.first
override fun hasNext() = index <= range.last
override fun next() = IndexedValue(index, this@withIndexInRange[index++])
}
}
- 使用 stdlib 中的
minByOrNull
方法找到最小值或为方便起见将其包装到另一个扩展方法中:
fun <T : Comparable<T>> Iterable<IndexedValue<T>>.indexOfMinOrNull() = minByOrNull { it.value }?.index
用法:
data.withIndexInRange(a..b).indexOfMinOrNull()
请注意,这会有一些性能损失(N 个额外对象的创建和 GC),但正如 Donald Knuth 所说:
Premature optimization is the root of all evil
所以,我相信更好的可读性是值得的。
我正在将一些较旧的 Java 代码重构到 Kotlin。有一个函数 returns Kotlin IntArray
中的元素在 [a
, b
] 范围内保持的最小值的索引。范围值默认为 0,数组大小为 - 1。
我想做一些类似...
return data.minOf().indexOf()
...但仅在 data
的 a
和 b
索引之间迭代。
函数如下:
// data is the IntArray property that I'm looping through.
fun absMinIndex(a: Int = 0, b: Int = (data.size - 1)) : Int {
var minVal = data[a]
var minIndex = 0
for (i in (a + 1)..b) {
val e = data[i]
if (e < minVal) {
minVal = e
minIndex = i
}
}
return maxIndex
}
这个 [for 循环] 通过从不访问超出范围的索引并且不生成副本 array/sub-array 很好地解决了这个问题。我想知道是否可以完成 'prettier'.
问题
是否有更惯用的 Kotlin 方法来迭代范围内的数组,而不会对我当前解决方案的性能产生负面影响?
为清楚起见编辑了一些代码。
正如 Tenfour04 所建议的,在不损失性能的情况下,您无能为力。但是按照你的想法,改变你的调用方式,你可以把它变成一个扩展函数。
fun IntArray.findIndexOfMinInRange(a: Int = 0, b: Int = this.size - 1): Int {
var maxVal = get(a)
var maxIndex = 0
for (i in (a + 1)..b) {
if (get(i) < maxVal) {
maxVal = get(i)
maxIndex = i
}
}
return maxIndex
}
//and call it like this:
data.findIndexOfMinInRange(0, 15) //or without anything in the parentheses for the default values
我要更改的一件事是函数内的变量名,我们搜索的是最小值,而不是最大值,还有最小值的索引,而不是最大索引。也许(也许很大)创建一个 data[it]
的 val 而不是访问它两次可能会更好(老实说不确定,我们会用 .get
换取几个字节的内存)。
所以总而言之,我可能会把它留在这里:
fun IntArray.findIndexOfMinInRange(fromIndex: Int = 0, toIndex: Int = this.size - 1): Int {
var min = get(fromIndex)
var indexOfMin = fromIndex
for(i in (fromIndex + 1)..toIndex){
val current = get(i)
if (current < min) {
min = current
indexOfMin = i
}
}
return indexOfMin
}
//would be called in the same way the one above
另外,请注意,如果您创建了一个特定大小的 IntArray
,并且没有完全填充它,它将为未填充的默认值保留 0
.如果你这样做:
val data = IntArray(6)
data[0] = 10
data[1] = 11
data[2] = 100
data[3] = 9
data[4] = 50
那么实际的数组就是[10, 11, 100, 9, 50, 0]
.
我相信这种方法会更地道:
- 使用
IntRange
作为输入参数 - 为
IntArray
定义扩展方法,提供自定义迭代器以遍历所需范围内的列表,将值包装到IndexedValue
:
fun IntArray.withIndexInRange(range: IntRange = 0..lastIndex) = Iterable {
require(range.first >= 0 && range.last <= lastIndex)
object : Iterator<IndexedValue<Int>> {
private var index = range.first
override fun hasNext() = index <= range.last
override fun next() = IndexedValue(index, this@withIndexInRange[index++])
}
}
- 使用 stdlib 中的
minByOrNull
方法找到最小值或为方便起见将其包装到另一个扩展方法中:
fun <T : Comparable<T>> Iterable<IndexedValue<T>>.indexOfMinOrNull() = minByOrNull { it.value }?.index
用法:
data.withIndexInRange(a..b).indexOfMinOrNull()
请注意,这会有一些性能损失(N 个额外对象的创建和 GC),但正如 Donald Knuth 所说:
Premature optimization is the root of all evil
所以,我相信更好的可读性是值得的。