假设我有一个命令式算法,它保留两个索引left
和right
并将它们从左向右和从右向左移动
var left = 0
var right = array.length - 1
while (left < right) { .... } // move left and right inside the loop
现在我想编写这个算法而不用可变索引。 我怎样才能做到这一点 ?你有这种算法的例子吗?我更喜欢非递归方法。
答案 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不能为此提供简单/优雅的解决方案。