Scalaには古いJava style for
インデックス付きループはありません。
// does not work
val xs = Array("first", "second", "third")
for (i=0; i<xs.length; i++) {
println("String #" + i + " is " + xs(i))
}
var
を使用せずに効率的に反復するにはどうすればよいですか?
あなたはこれを行うことができます
val xs = Array("first", "second", "third")
val indexed = xs zipWithIndex
for (x <- indexed) println("String #" + x._2 + " is " + x._1)
しかし、リストは2回走査されます-非常に効率的ではありません。
2回トラバースするよりもはるかに悪いことに、ペアの中間配列が作成されます。 view
を使用できます。 collection.view
、反復中に後続の呼び出しが遅延して動作していると考えることができます。完全に実現された適切なコレクションを取得したい場合は、最後にforce
を呼び出します。ここでは役に立たず、費用がかかります。だからあなたのコードを
for((x,i) <- xs.view.zipWithIndex) println("String #" + i + " is " + x)
Scaladoesにはfor
ループの構文があります:
for (i <- 0 until xs.length) ...
または単に
for (i <- xs.indices) ...
ただし、効率性も求めました。 Scala for
構文は、実際にmap
、foreach
などの高次メソッドの構文シュガーです。 、場合によっては、これらのループは非効率になることがあります。たとえば、 Scalaでfor-comprehensionsとloopsを最適化する方法
(良いニュースは、Scalaチームがこれを改善するために取り組んでいるということです。バグトラッカーの問題は次のとおりです。 https://issues.scala-lang.org/browse/ SI-46 )
最大限の効率を得るには、while
ループを使用するか、var
の使用を削除することを主張する場合、末尾再帰を使用できます。
import scala.annotation.tailrec
@tailrec def printArray(i: Int, xs: Array[String]) {
if (i < xs.length) {
println("String #" + i + " is " + xs(i))
printArray(i+1, xs)
}
}
printArray(0, Array("first", "second", "third"))
optional@tailrec
アノテーションは、メソッドが実際に末尾再帰であることを確認するのに役立ちます。 Scalaコンパイラは、末尾再帰呼び出しをwhileループに相当するバイトコードに変換します。
もう1つの方法:
scala> val xs = Array("first", "second", "third")
xs: Array[Java.lang.String] = Array(first, second, third)
scala> for (i <- xs.indices)
| println(i + ": " + xs(i))
0: first
1: second
2: third
実際、scalaにはインデックス付きの古いJavaスタイルのループがあります。
_scala> val xs = Array("first","second","third")
xs: Array[Java.lang.String] = Array(first, second, third)
scala> for (i <- 0 until xs.length)
| println("String # " + i + " is "+ xs(i))
String # 0 is first
String # 1 is second
String # 2 is third
_
ここで、_0 until xs.length
_または0.until(xs.length)
は、ループに適したRichInt
を返すRange
メソッドです。
また、to
でループを試すことができます:
_scala> for (i <- 0 to xs.length-1)
| println("String # " + i + " is "+ xs(i))
String # 0 is first
String # 1 is second
String # 2 is third
_
これはどう?
val a = Array("One", "Two", "Three")
a.foldLeft(0) ((i, x) => {println(i + ": " + x); i + 1;} )
出力:
0: One
1: Two
2: Three
実際、コレクションでzipWithIndex
を呼び出すと、コレクションを走査し、ペアの新しいコレクションも作成されます。これを回避するには、コレクションのイテレータでzipWithIndex
を呼び出すだけです。これは、反復中にインデックスを追跡する新しいイテレータを返すだけなので、追加のコレクションや追加のトラバースを作成する必要はありません。
こうやって scala.collection.Iterator.zipWithIndex
は現在2.10.3で実装されています。
def zipWithIndex: Iterator[(A, Int)] = new AbstractIterator[(A, Int)] {
var idx = 0
def hasNext = self.hasNext
def next = {
val ret = (self.next, idx)
idx += 1
ret
}
}
これは、コレクションにビューを作成するよりも少し効率的です。
scalaでのループは非常に簡単です。exの任意の配列を作成します。
val myArray = new Array[String](3)
myArray(0)="0";
myArray(1)="1";
myArray(2)="2";
ループの種類、
for(data <- myArray)println(data)
for (i <- 0 until myArray.size)
println(i + ": " + myArray(i))
私は次のアプローチを持っています
object HelloV2 {
def main(args: Array[String]) {
//Efficient iteration with index in Scala
//Approach #1
var msg = "";
for (i <- args.indices)
{
msg+=(args(i));
}
var msg1="";
//Approach #2
for (i <- 0 until args.length)
{
msg1 += (args(i));
}
//Approach #3
var msg3=""
args.foreach{
arg =>
msg3 += (arg)
}
println("msg= " + msg);
println("msg1= " + msg1);
println("msg3= " + msg3);
}
}
StdlibにはTupleガベージを作成せずにそれを行うものは何もありませんが、独自のコードを作成するのはそれほど難しくありません。残念なことに、適切なCanBuildFrom暗黙の降雨を適用して、適用されるコレクションのタイプでそのようなことを一般化する方法を考え出すことを決して気にしませんでしたが、可能であれば、誰かが私たちを啓発するでしょう。 :)
def foreachWithIndex[A](as: Traversable[A])(f: (Int,A) => Unit) {
var i = 0
for (a <- as) {
f(i, a)
i += 1
}
}
def mapWithIndex[A,B](in: List[A])(f: (Int,A) => B): List[B] = {
def mapWithIndex0(in: List[A], gotSoFar: List[B], i: Int): List[B] = {
in match {
case Nil => gotSoFar.reverse
case one :: more => mapWithIndex0(more, f(i, one) :: gotSoFar, i+1)
}
}
mapWithIndex0(in, Nil, 0)
}
// Tests....
@Test
def testForeachWithIndex() {
var out = List[Int]()
ScalaUtils.foreachWithIndex(List(1,2,3,4)) { (i, num) =>
out :+= i * num
}
assertEquals(List(0,2,6,12),out)
}
@Test
def testMapWithIndex() {
val out = ScalaUtils.mapWithIndex(List(4,3,2,1)) { (i, num) =>
i * num
}
assertEquals(List(0,3,4,3),out)
}
反復するいくつかの方法:
scala> xs.foreach (println)
first
second
third
foreach、および同様のマップ。何かを返します(println、Unit、つまりList of Unitsの関数の結果)
scala> val lens = for (x <- xs) yield (x.length)
lens: Array[Int] = Array(5, 6, 5)
インデックスではなく要素を操作します
scala> ("" /: xs) (_ + _)
res21: Java.lang.String = firstsecondthird
折り畳み
for(int i=0, j=0; i+j<100; i+=j*2, j+=i+2) {...}
再帰で行うことができます:
def ijIter (i: Int = 0, j: Int = 0, carry: Int = 0) : Int =
if (i + j >= 100) carry else
ijIter (i+2*j, j+i+2, carry / 3 + 2 * i - 4 * j + 10)
キャリーパートは、iとjで何かをするためのほんの一例です。 Intである必要はありません。
より単純なもの、通常のforループに近いもの:
scala> (1 until 4)
res43: scala.collection.immutable.Range with scala.collection.immutable.Range.ByOne = Range(1, 2, 3)
scala> (0 to 8 by 2)
res44: scala.collection.immutable.Range = Range(0, 2, 4, 6, 8)
scala> (26 to 13 by -3)
res45: scala.collection.immutable.Range = Range(26, 23, 20, 17, 14)
または注文なし:
List (1, 3, 2, 5, 9, 7).foreach (print)
SeqLike.scala のtransform
の実装に触発されたシンプルで効率的な方法
var i = 0
xs foreach { el =>
println("String #" + i + " is " + xs(i))
i += 1
}
提案されたソリューションは、コレクションを明示的に反復するか、コレクションを関数に詰め込むという事実に苦しんでいます。 Scalaの通常のイディオムに固執し、インデックスを通常のmap-またはforeach-methods内に配置する方が自然です。これはメモ化を使用して実行できます。結果のコードは次のようになります。
myIterable map (doIndexed(someFunction))
この目的を達成する方法を次に示します。次のユーティリティを検討してください。
object TraversableUtil {
class IndexMemoizingFunction[A, B](f: (Int, A) => B) extends Function1[A, B] {
private var index = 0
override def apply(a: A): B = {
val ret = f(index, a)
index += 1
ret
}
}
def doIndexed[A, B](f: (Int, A) => B): A => B = {
new IndexMemoizingFunction(f)
}
}
必要なのはこれだけです。これは、たとえば次のように適用できます。
import TraversableUtil._
List('a','b','c').map(doIndexed((i, char) => char + i))
リストになります
List(97, 99, 101)
このように、効果的な機能をラップすることを犠牲にして、通常のTraversable機能を使用できます。楽しい!