迭代时返回集合元素的双索引

lik*_*ern 4 iteration iterator kotlin

在Kotlin文档中,我发现了以下示例:

for ((index, value) in array.withIndex()) {
    println("the element at $index is $value")
}
Run Code Online (Sandbox Code Playgroud)

是否有可能(以及如何)使用2D矩阵进行类似的操作:

for ((i, j, value) in matrix2D.withIndex()) {
    // but iterate iver double index: i - row, j - column
    if (otherMatrix2D[i, j] > value) doSomething()
}
Run Code Online (Sandbox Code Playgroud)

如何在Kotlin类中支持此功能?

hot*_*key 5

这两种语言功能用于实现您想要的行为:

  • For循环可以与任何具有提供迭代器的方法的类一起使用.

    for (item in myItems) { ... }
    
    Run Code Online (Sandbox Code Playgroud)

    如果myItems函数iterator()返回函数hasNext(): Boolean和函数,则此代码将编译next().

    通常它是一个Iterable<SomeType>实现(某些集合),但您可以将iterator()方法作为扩展添加到现有类,并且您也可以在for循环中使用该类.

  • 对于解构声明,项类型应具有componentN()函数.

    val (x, y, z) = item
    
    Run Code Online (Sandbox Code Playgroud)

    这里编译器期望itemcomponent1(),component2()component3()功能.您也可以使用data,它们生成了这些函数.

    for循环中的解构以类似的方式工作:迭代器next()返回的类型必须具有componentN()函数.


示例实现(不假装性能最佳,见下文):


Michael提供的UPD解决方案实际上表现更好(运行此测试,差异大约是2x到3x),因此它更适合性能关键代码.


Mic*_*ael 5

虽然miensolhotkey提出的解决方案是正确的,但它是迭代矩阵的最低效方法.例如,溶液热键使M * N的分配Cell<T>M的分配List<Cell<T>>IntRange加一个分配List<List<Cell<T>>>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])
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

您可以通过以下方式调用此函数:

matrix.forEachIndexed { i, j, value ->
  if (otherMatrix[i, j] > value) doSomething()
}
Run Code Online (Sandbox Code Playgroud)

使用破坏性声明进行迭代

如果for由于某种原因想要使用具有破坏性声明的传统-loop,那么存在一种更有效但是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
  }
}
Run Code Online (Sandbox Code Playgroud)

您可以使用此函数迭代for-loop中的矩阵:

for ((i, j, item) in matrix.withIndex()) {
   if (otherMatrix[i, j] > value) doSomething()
}
Run Code Online (Sandbox Code Playgroud)

这个解决方案比第一个解决方案效率低得多,并且因为可变性而不那么强大Cell,所以我建议你使用第一个解决方案.