如何在Scala中映射数组时获取元素索引?

时间:2012-02-04 01:29:35

标签: arrays scala map scala-collections

让我们考虑一个简单的映射示例:


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

我需要的不是myFn(s: String): String,而是myFn(s: String, n: Int): String,其中n将是sa的索引。在这种特殊情况下,myFn会期望第二个参数对于s ==“One”为0,对于s ==“Two”为1,对于s ==“Three”为2。我怎样才能做到这一点?

3 个答案:

答案 0 :(得分:69)

取决于您是否想要方便或速度。

慢速:

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
}

(在带有Java 1.6u37的Scala 2.10.1中,如果声称“可能最快”被称为花费1倍时间进行一个简单的字符串操作(将长字符串截断为几个字符),那么“慢”需要2倍长,“更快”每个花费的时间延长1.3倍,“肯定”只花费0.5倍的时间。)

答案 1 :(得分:4)

一般提示:大量使用.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)

答案 2 :(得分:3)

这个怎么样?我认为它应该很快而且很漂亮。但我不是斯卡拉速度专家......

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