Перебрать два списка (разной длины) в обратном порядке в Scala

Каков наиболее эффективный способ перебора двух списков (разной длины) в обратном направлении в Scala.

Итак, для двух списков

List(a,b,c) and List(1,2)

пары были бы

(c,2) and (b,1)

Примечание. Я бы предпочел не делать реверс каждого списка.


person ademartini    schedule 30.01.2014    source источник
comment
а что будет (a, ?)?   -  person om-nom-nom    schedule 30.01.2014
comment
a будет игнорироваться, как и застежка-молния. Но это обратная молния.   -  person ademartini    schedule 30.01.2014
comment
List('a', 'b', 'c').reverse zip List(1,2).reverse. Думаю, вы сами ответили на свой вопрос.   -  person Brian    schedule 30.01.2014
comment
Как я сказал ниже, есть ли что-то более эффективное? Реверс включает копию.   -  person ademartini    schedule 30.01.2014
comment
Глупо, что меня минусуют ... Я сказал самый эффективный способ в начале своего поста.   -  person ademartini    schedule 30.01.2014
comment
@ademartini некоторые могут думать об эффективности как о затраченных линиях, а не затраченных циклах процессора   -  person om-nom-nom    schedule 31.01.2014
comment
Я думал, можно ли использовать рекурсию.   -  person tuxdna    schedule 31.01.2014
comment
Вам важно, в каком порядке создаются пары?   -  person The Archetypal Paul    schedule 31.01.2014


Ответы (4)


Простой способ:

 List('a','b','c').reverse zip List(1,2).reverse

Однако перевернуть список можно O(n), если вы беспокоитесь об эффективности.

Согласно scaladoc List, использование reverseIterator может быть более эффективным. Таким образом, вы не создаете новый список, как с reverse, а просматриваете его, продолжая итерацию. Это было бы:

val it = list1.reverseIterator zip list2.reverseIterator  //returns an Iterator you can force
it.toList // List((c,2), (b,1))
person Chirlo    schedule 30.01.2014
comment
reverseIterator вызывает reverse внутренне, который создает новый список, так что экономия не так уж велика - person smac89; 27.04.2017

Используя параллельные коллекции,

def parRevZip (a: List[String], b: List[Int]) = {

  val max = Math.max(a.size, b.size)
  val n = Math.abs(a.size - b.size)

  if (a.size > b.size)
    (max to n+1 by -1).par.map { i => (a(i-1), b(i-n-1)) }
  else
    (max to n+1 by -1).par.map { i => (a(i-n-1), b(i-1)) }
}

Принимая во внимание разные значения индекса для списков, возможно, разного размера, этот подход выбирает и объединяет одинаковое количество элементов, начиная с конца каждого списка.

Производительность требует тщательной оценки; для небольших списков простой реверс и сжатие могут оказаться намного проще и эффективнее; наоборот, для больших списков этот параллельный подход может быть интересен.

Уточнение кода

def parRevZip[A,B] (a: List[A], b: List[B]) = {

  val aSize = a.size
  val bSize = b.size

  val max = Math.max(aSize, bSize)
  val n = Math.abs(aSize - bSize)

  if (aSize > bSize)
    (max-1 to n by -1).par.map { i => (a(i), b(i-n)) }
  else
    (max-1 to n by -1).par.map { i => (a(i-n), b(i)) }
}

Использование нерекурсивных коллекций

Удобные неизменяемые коллекции здесь, где вычисление размера равно O(1) (или квазипостоянному) (см. Рекурсивные коллекции в Scala, такие как List ) включают, например, Array. Следовательно,

def parRevZip[A,B] (a: Array[A], b: Array[B])

что не соответствует дальнейшему требованию обработки списков.

person elm    schedule 30.01.2014
comment
Нахождение длины List — это O(N) операция. Scala API говорит: Примечание: выполнение length может занять время, пропорциональное длине последовательности. - person tuxdna; 31.01.2014
comment
@tuxdna, абсолютно верно :) Действительно, список — это рекурсивная структура (stackoverflow.com/a/8197826/3189923); в этом ответе необходимо учитывать нерекурсивную структуру ... возможно, массив? - person elm; 31.01.2014
comment
Кроме того, a(i) занимает время, пропорциональное i. Vector было бы более подходящим, если вы делаете произвольный доступ. - person Chirlo; 31.01.2014
comment
Спасибо @Chirlo :), так что этот ответ возможен, если List не используется :) Vector и Array - хорошие кандидаты... - person elm; 31.01.2014

Я думаю, вы имеете в виду это:

val a = List(1, 2, 3)
val b = List(8, 9)

val result = a.reverse zip b.reverse
person Rado Buransky    schedule 30.01.2014
comment
Есть ли что-то более эффективное, чем это? Я не хочу копировать каждый список для реверса. - person ademartini; 30.01.2014
comment
Scala List реализован как однонаправленный связанный список, и поэтому он не предназначен для навигации в обратном направлении. Чтобы ответить на эффективность, выходит за рамки этой области. Вас беспокоит сложность памяти, временная сложность, эти списки более или менее статичны или они часто меняются... попробуйте посмотреть здесь: scala-lang.org/docu/files/collections-api/collections_40.html - person Rado Buransky; 30.01.2014
comment
Это, безусловно, в рамках. Это был первоначальный вопрос. - person ademartini; 30.01.2014
comment
Да, но, как я уже сказал, вы должны быть более конкретными, что означает эффективный. Каков ваш вариант использования. Каждая структура данных имеет свои плюсы и минусы. Ссылка, которую я отправил вам, должна помочь вам принять правильное решение. - person Rado Buransky; 30.01.2014

Вот моя попытка решить эту проблему. Исходные списки a и b не дублируются. Операция O(N) связана с List.size:

object test extends App {
  val a = List("a", "b", "c")                     //> a  : List[String] = List(a, b, c)
  val b = List(1, 2)                              //> b  : List[Int] = List(1, 2)
  val aSize = a.size                              //> aSize  : Int = 3
  val bSize = b.size                              //> bSize  : Int = 2

  // find which is smaller and which is bigger list
  val (smaller, bigger) = if (aSize < bSize) (a, b) else (b, a)
                                                  //> smaller  : List[Any] = List(1, 2)
                                                  //| bigger  : List[Any] = List(a, b, c)

  // skip the extra entries from head of bigger list
  val truncated = bigger.drop(Math.abs(aSize - bSize))
                                                  //> truncated  : List[Any] = List(b, c)

  val result = if (a == smaller)
    smaller.zip(truncated).reverse
  else
    truncated.zip(smaller).reverse                //> result  : List[(Any, Any)] = List((c,2), (b,1))

}
person tuxdna    schedule 31.01.2014