如何从左到右,从右到左遍历数组?

时间:2017-02-16 11:23:00

标签: scala functional-programming traversal

假设我有一个命令式算法,它保留两个索引leftright并将它们从左向右和从右向左移动

var left  = 0
var right = array.length - 1
while (left < right) { .... } // move left and right inside the loop

现在我想编写这个算法而不用可变索引。 我怎样才能做到这一点 ?你有这种算法的例子吗?我更喜欢非递归方法。

3 个答案:

答案 0 :(得分:4)

您可以在列表与其反向之间映射元素对,然后从左到右遍历该对列表,并在条件满足时继续使用:

val list = List(1, 2, 3, 4, 5)
val zipped = list zip list.reverse
val filtered = zipped takeWhile { case (a, b) => (a < b) }

filtered的值为List((1, 5), (2, 4))。 现在,您可以使用这些元素做任何您需要的事情:

val result = filtered map {
  case (a, b) => 
    // do something with each left-right pair, e.g. sum them
    a + b
}

println(result) // List(6, 6)

如果您需要某种依赖于上下文的操作(即每个操作)    迭代取决于前一个的结果)然后你必须    使用更强大的抽象(monad),但是如果让我们不去那里    这对你来说已经足够了。如其他人所指出的那样,更好的是简单地使用递归,但你说这不是一种选择。

编辑:

版本没有额外的反转通道,只有elem(长度 - 索引)的恒定时间访问:

val list = List(1, 2, 3, 4, 5)
val zipped = list.view.zipWithIndex
val filtered = zipped takeWhile { case (a, index) => (a < list(list.length - 1 - index)) }

println(filtered.toList)  // List((1, 0), (2, 1))

val result = filtered map {
  case (elem, index) => // do something with each left-right pair, e.g. sum them
    val (a, b) = (elem, list(list.length - 1 - index))
    a + b
}

println(result.toList) // List(6, 6)

答案 1 :(得分:3)

使用reverseIterator

scala> val arr = Array(1,2,3,4,5)
arr: Array[Int] = Array(1, 2, 3, 4, 5)

scala> arr.iterator.zip(arr.reverseIterator).foreach(println)
(1,5)
(2,4)
(3,3)
(4,2)
(5,1)

此功能对IndexedSeq个集合很有效,Array可隐式转换为。{/ p>

答案 2 :(得分:1)

这实际上取决于每次迭代需要做什么,但这里需要考虑一下。

array.foldRight(0){case (elem, index) =>
  if (index < array.length/2) {
    /* array(index) and elem are opposite elements in the array */
    /* do whatever (note: requires side effects) */
    index+1
  } else index // do nothing
} // ignore result

上行:仅遍历数组一次并且没有可变变量。

下行:需要副作用(但在您的示例中暗示)。此外,如果它只穿过阵列的一半会更好,但这需要提前突破,Scala不能为此提供简单/优雅的解决方案。