How to traverse array from both left to right and from right to left?

房东的猫 提交于 2020-01-04 01:57:29

问题


Suppose I have an imperative algorithm that keeps two indices left and right and moves them from left to right and from right to left

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

Now I would like to write this algorithm without mutable indices. How can I do that ? Do you have any examples of such algorithms ? I would prefer a non-recursive approach.


回答1:


You can map pairs of elements between your list and its reverse, then go from left to right through that list of pairs and keep taking as long as your condition is satisfied:

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

Value of filtered is List((1, 5), (2, 4)). Now you can do whatever you need with those elements:

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)

If you need some kind of context dependant operation (that is, each iteration depends on the result of the previous one) then you have to use a more powerful abstraction (monad), but let's not go there if this is enough for you. Even better would be to simply use recursion, as pointed out by others, but you said that's not an option.

EDIT:

Version without extra pass for reversing, only constant-time access for elem(length - index):

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)



回答2:


Use 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)

This function is efficient on IndexedSeq collections, which Array is implicitly convertible to.




回答3:


It really depends on what needs to be done at each iteration, but here's something to think about.

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

Upside: Traverse the array only once and no mutable variables.

Downside: Requires side effects (but that was implied in your example). Also, it'd be better if it traversed only half the array, but that would require early breakout and Scala doesn't offer an easy/elegant solution for that.



来源:https://stackoverflow.com/questions/42272546/how-to-traverse-array-from-both-left-to-right-and-from-right-to-left

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!