Как получить индекс элемента при сопоставлении массива в Scala?
Рассмотрим простой пример сопоставления:
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 == "Три". Как я могу достичь этого?
Ответы
Ответ 1
Зависит от того, хотите ли вы удобство или скорость.
Slow:
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 раз для тривиальной операции строки (усечение длинной строки на несколько символов), тогда "медленный" занимает 2x больше, "быстрее", каждый из них занимает 1,3 раза дольше, и "наверняка" занимает всего 0,5 раза больше времени.)
Ответ 2
Общий совет: используйте метод .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
выше включает в себя де-tupling, а затем функцию приложения. Это вынуждает использовать некоторые локальные переменные. Вы можете избежать этого, используя некоторое колдовство более высокого порядка - преобразовать регулярную функцию в один приемный кортеж, а затем передать его на map
.
scala> Array("nami", "zoro", "usopp").zipWithIndex.map(Function.tupled(myFun))
res24: Array[java.lang.String] = Array(nami0, zoro1, usopp2)
Ответ 3
Как насчет этого? Я думаю, что это должно быть быстро, и это красиво. Но я не эксперт по скорости Scala...
a.foldLeft(0) ((i, x) => {myFn(x, i); i + 1;} )