在Kotlin文档中,我找到了以下示例:
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}
是否有可能(以及如何)与2D矩阵相似:
for ((i, j, value) in matrix2D.withIndex()) {
// but iterate iver double index: i - row, j - column
if (otherMatrix2D[i, j] > value) doSomething()
}
如何在Kotlin课程中支持此功能?
答案 0 :(得分:5)
这两种语言功能用于实现您想要的行为:
For循环可以与任何具有方法that provides an iterator。
的类一起使用for (item in myItems) { ... }
如果myItems
函数iterator()
返回函数hasNext(): Boolean
和next()
,则会编译此代码。
通常它是Iterable<SomeType>
实现(某些集合),但您可以将iterator()
方法作为extension添加到现有类中,并且您将能够使用该类for-loops也是如此。
对于destructuring declaration,项目类型应具有componentN()
个功能。
val (x, y, z) = item
此处编译器希望item
具有component1()
,component2()
和component3()
函数。您也可以使用data
classes生成这些函数。
for循环中的解构以类似的方式工作:迭代器的next()
返回的类型必须具有componentN()
个函数。
<小时/> 示例实现(不假装性能最佳,见下文):
具有解构支持的类:
class Cell<T>(val i: Int, val j: Int, val item: T) {
operator fun component1() = i
operator fun component2() = j
operator fun component3() = item
}
或使用data
class:
data class Cell<T>(val i: Int, val j: Int, val item: T)
返回List<Cell<T>>
的函数(写为扩展名,但也可以是成员函数):
fun <T> Matrix<T>.withIndex() =
(0 .. height - 1).flatMap { i ->
(0 .. width - 1). map { j ->
Cell(i, j, this[i, j])
}
}
用法:
for ((i, j, item) in matrix2d.withIndex()) { ... }
UPD Michael提供的解决方案实际上表现更好(运行this test,差异大约是2x到3x),因此它更适合性能关键代码。
答案 1 :(得分:5)
虽然miensol和hotkey提出的解决方案是正确的,但它是迭代矩阵的最低效方法。例如,hotkey的解决方案M * N
分配Cell<T>
加M
List<Cell<T>>
和IntRange
分配加List<List<Cell<T>>>
的{{1}}分配}和IntRange
。此外,列表会在添加新单元格时调整大小,因此会发生更多分配。这只是迭代矩阵的分配太多了。
我建议你实现一个非常相似且非常有效的扩展功能,类似于Array<T>.forEachIndexed
。该解决方案根本不进行任何分配,并且与编写嵌套的for
循环一样有效。
inline fun <T> Matrix<T>.forEachIndexed(callback: (Int, Int, T) -> Unit) {
for (i in 0..cols - 1) {
for (j in 0..rows - 1) {
callback(i, j, this[i, j])
}
}
}
您可以通过以下方式调用此功能:
matrix.forEachIndexed { i, j, value ->
if (otherMatrix[i, j] > value) doSomething()
}
如果你想使用传统的for
- 循环与破坏性声明由于某种原因存在一种更有效但是hacky解决方案的方法。它使用序列而不是分配多个列表,只创建Cell
的单个实例,但Cell
本身是可变的。
data class Cell<T>(var i: Int, var j: Int, var value: T)
fun <T> Matrix<T>.withIndex(): Sequence<Cell<T>> {
val cell = Cell(0, 0, this[0, 0])
return generateSequence(cell) { cell ->
cell.j += 1
if (cell.j >= rows) {
cell.j = 0
cell.i += 1
if (cell.i >= cols) {
return@generateSequence null
}
}
cell.value = this[cell.i, cell.j]
cell
}
}
您可以使用此函数迭代for
循环中的矩阵:
for ((i, j, item) in matrix.withIndex()) {
if (otherMatrix[i, j] > value) doSomething()
}
这个解决方案的效率远低于第一个解决方案,并且由于可变Cell
而不那么强大,所以我建议你使用第一个解决方案。
答案 2 :(得分:3)
以下方法:
data class Matrix2DValue<T>(val x: Int, val y: Int, val value: T)
fun withIndex(): Iterable<Matrix2DValue<T>> {
//build the list of values
}
允许您将for
写为:
for ((x, y, value) in matrix2d.withIndex()) {
println("value: $value, x: $x, y: $y")
}
请记住,您声明data class
属性的顺序定义(x, y, value)
的值 - 而不是for
变量名称。您可以找到更多information about destructuring in the Kotlin documentation。