在迭代

时间:2016-04-05 18:58:18

标签: iterator iteration kotlin

在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课程中支持此功能?

3 个答案:

答案 0 :(得分:5)

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

  • For循环可以与任何具有方法that provides an iterator

    的类一起使用
    for (item in myItems) { ... }
    

    如果myItems函数iterator()返回函数hasNext(): Booleannext(),则会编译此代码。

    通常它是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)

虽然miensolhotkey提出的解决方案是正确的,但它是迭代矩阵的最低效方法。例如,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