Как получить индекс элемента при отображении массива в Scala? - PullRequest
25 голосов
/ 04 февраля 2012

Давайте рассмотрим простой пример отображения:


  val a = Array("One", "Two", "Three")
  val b = a.map(s => myFn(s))

Мне нужно использовать здесь не myFn(s: String): String, а myFn(s: String, n: Int): String, где n будет индексом s в a.В этом конкретном случае myFn ожидает, что второй аргумент будет равен 0 для s == «Один», 1 для s == «Два» и 2 для s == «Три».Как мне этого добиться?

Ответы [ 3 ]

69 голосов
/ 04 февраля 2012

Зависит от того, хотите ли вы удобство или скорость.

Медленно:

a.zipWithIndex.map{ case (s,i) => myFn(s,i) }

Быстрее:

for (i <- a.indices) yield myFn(a(i),i)

{ var i = -1; a.map{ s => i += 1; myFn(s,i) } }

Возможно, самое быстрое:

Array.tabulate(a.length){ i => myFn(a(i),i) }

Если нет, то это, безусловно, так:

val b = new Array[Whatever](a.length)
var i = 0
while (i < a.length) {
  b(i) = myFn(a(i),i)
  i += 1
}

(В Scala 2.10.1 с Java 1.6u37, если объявлено, что "возможно самый быстрый", то для тривиальной строковой операции требуется 1 раз (усечениедлинная строка (до нескольких символов), затем «медленный» занимает в 2 раза больше, «быстрее» - в 1,3 раза дольше, а «наверняка» - только в 0,5 раза больше времени.)

4 голосов
/ 04 февраля 2012

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

scala> def myFun(s: String, i: Int) = s + i
myFun: (s: String, i: Int)java.lang.String

scala> Array("nami", "zoro", "usopp")
res17: Array[java.lang.String] = Array(nami, zoro, usopp)

scala> res17.iterator.zipWithIndex
res19: java.lang.Object with Iterator[(java.lang.String, Int)]{def idx: Int; def idx_=(x$1: Int): Unit} = non-empty iterator

scala> res19 map { case (k, v) => myFun(k, v) }
res22: Iterator[java.lang.String] = non-empty iterator

scala> res22.toArray
res23: Array[java.lang.String] = Array(nami0, zoro1, usopp2)

Имейте в виду, что итераторы являются изменяемыми и, следовательно, однажды использованные не могут использоваться снова.


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

scala> Array("nami", "zoro", "usopp").zipWithIndex.map(Function.tupled(myFun))
res24: Array[java.lang.String] = Array(nami0, zoro1, usopp2)
3 голосов
/ 30 мая 2013

А как насчет этого? Я думаю, что это должно быть быстро и красиво. Но я не эксперт по скорости Scala ...

a.foldLeft(0) ((i, x) => {myFn(x, i); i + 1;} )
...