【问题标题】:How to traverse array from both left to right and from right to left?如何从左到右和从右到左遍历数组?
【发布时间】:2022-01-23 13:35:19
【问题描述】:

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

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

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

【问题讨论】:

  • 你可以使用递归方法...
  • 哦,是的。谢谢。不过,我更喜欢非递归方法。
  • 如果你想使用循环,你必须改变任何变量(至少是一些辅助变量),以获得退出条件。顺便提一句。你为什么不想使用递归?这是用不可变值做事情的功能性方式! (请更新您的问题)
  • 您必须使用递归或可变索引。你怎么能记住你正在处理的元素

标签: scala functional-programming traversal


【解决方案1】:

你可以在你的列表和它的反向元素之间映射元素对,然后从左到右遍历该对列表,只要你的条件得到满足就继续:

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

【讨论】:

  • 我喜欢使用reverse 的想法。然而,它为算法增加了一个数组传递。我想知道我是否可以使用view/stream 懒惰地执行reverse
  • 就复杂性而言,无论如何你都在 O(n) 。名单真的那么大吗?
  • @Michael:如果您想通过索引访问元素(例如在您问题中的命令式版本中),也许val r = 0 to (a.length - 1); val indices = r zip r.reverse; r.map {case (left, right) =&gt; …} 可能值得考虑?
  • @Marth 还有zipWithIndex,您可以在压缩的配对列表中调用它。这样你就会得到一个List[(Int, Int), Int],它在 _1 上有对,在 _2 上有索引
  • @Michael 我并不是想成为“那个人”,但我们到底在做什么呢?你得到了很多回应,很多有效的想法和解决方案,以及大量的讨论。引用 StackOverflow 的话:“请避免在 cmets 中进行扩展讨论。您想自动将此讨论移至聊天吗?”。如果您的问题没有得到可接受的答案,请更新它,我们很乐意为您的问题提供完整的解决方案。 SO 的意思是一个具体的问题——具体的答案之类的东西,而不是“给我 1000 个选项来实现这一点,然后让我们讨论它们”。
【解决方案2】:

使用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 可以隐式转换为。

【讨论】:

    【解决方案3】:

    这实际上取决于每次迭代需要做什么,但这里有一些需要考虑的事情。

    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 并没有为此提供简单/优雅的解决方案。

    【讨论】:

      【解决方案4】:
          myarray = [1,2,3,4,5,6]
          rmyarray  = myarray[::-1]
          
          Final_Result = []
          
          for i in range(len(myarray)//2):
              Final_Result.append(myarray[i])
              Final_Result.append(rmyarray[i])
          
          print(Final_Result)
      
      # This is the simple approach I think ?.
      

      【讨论】:

      • 请不要发布仅代码的答案。未来的读者将不胜感激看到解释为什么它回答了问题,而不是不得不从代码中推断出来。而且,由于这是一个老问题,请解释它如何补充其他答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-05-08
      • 1970-01-01
      • 2022-01-18
      • 1970-01-01
      • 2017-07-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多