2017-02-16 28 views
3

Предположим, что у меня есть императив алгоритм, который держит два индекса left и right и перемещает их слева направо и справа налевоКак перемещаться по массиву слева направо и справа налево?

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

Теперь я хотел бы написать этот алгоритм без изменяемых показателей. Как я могу это сделать? Есть ли у вас примеры таких алгоритмов? Я бы предпочел нерекурсивный подход.

+2

Вы можете использовать рекурсивный подход ... –

+0

О, да. Благодарю. Однако я бы предпочел нерекурсивный подход. – Michael

+0

Если вы хотите использовать цикл, вам нужно будет изменить любую переменную (по крайней мере, некоторую helpervariable), чтобы получить условие выхода. Btw. почему вы не хотите использовать рекурсию? Это функциональный способ делать вещи с неизменными ценностями! (Пожалуйста, обновите свой вопрос) –

ответ

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) 

Если вам нужно какое-то зависит от контекста операции (то есть, каждый итерации зависит от результата предыдущего), то вы должны использования более мощная абстракция (монада), но давайте не будем туда, если вам этого достаточно. Еще лучше было бы просто использовать рекурсию, как отмечали другие, но вы сказали, что это не вариант.

EDIT:

версия без дополнительного прохода для реверсирования, только постоянное время доступ для эля (длина - индекс):

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) 
+0

Мне нравится идея использования 'reverse'. Однако он добавляет еще один массив в алгоритм. Мне интересно, может ли я сделать «обратный» лениво, используя «view/stream», например. – Michael

+0

С точки зрения сложности вы в O (n) в любом случае. Действительно ли список такой большой? – slouc

+0

@Michael: Если вы хотите получить доступ к элементам по их индексам (например, в императивной версии вашего вопроса), возможно, «val r = 0 to (a.length - 1); индексы val = r zip r.reverse; r.map {case (left, right) => ...} 'можно было бы рассмотреть? – Marth

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 неявно конвертируется в.

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 не предложит для этого легкое/элегантное решение.

 Смежные вопросы

  • Нет связанных вопросов^_^