web-dev-qa-db-ja.com

Scalaで配列をマッピングするときに要素のインデックスを取得するにはどうすればよいですか?

簡単なマッピングの例を考えてみましょう:

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

ここではmyFn(s: String): StringではなくmyFn(s: String, n: Int): Stringを使用する必要があります。ここで、nsaのインデックスになります。この特定のケースでは、myFnは2番目の引数がs == "One"の場合は0、s == "Two"の場合は1、s == "Three"の場合は2であると想定します。どうすればこれを達成できますか?

25
Ivan

利便性とスピードのどちらを希望するかによります。

スロー:

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 with Java 1.6u37の場合、「最速」が宣言されている場合、自明な文字列操作に1倍の時間がかかると宣言されます(長い文字列を数文字に)、「遅い」には2倍、「速い」には1.3倍、「確かに」には0.5倍の時間がかかります。)

71
Rex Kerr

一般的なヒント:.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)
4
missingfaktor

これはどうですか?速くてきれいだと思います。しかし、私はScala速度についての専門家ではありません...

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