Scala에서 인덱스를 사용한 효율적인 반복
Scala에는 for
인덱스 가있는 오래된 Java 스타일 루프 가 없기 때문에
// does not work
val xs = Array("first", "second", "third")
for (i=0; i<xs.length; i++) {
println("String #" + i + " is " + xs(i))
}
var
's 를 사용하지 않고 어떻게 효율적으로 반복 할 수 있습니까?
당신은 이것을 할 수 있습니다
val xs = Array("first", "second", "third")
val indexed = xs zipWithIndex
for (x <- indexed) println("String #" + x._2 + " is " + x._1)
그러나 목록은 두 번 순회됩니다-그다지 효율적이지 않습니다.
두 번 순회하는 것보다 훨씬 더 나쁜 것은 쌍의 중간 배열을 생성한다는 것입니다. 사용할 수 있습니다 view
. 을 수행하면 collection.view
후속 호출이 반복 중에 느리게 작동한다고 생각할 수 있습니다. 완전히 실현 된 적절한 컬렉션을 되 찾으 force
려면 마지막에 전화 하십시오. 여기에서는 쓸모없고 비용이 많이 듭니다. 따라서 코드를
for((x,i) <- xs.view.zipWithIndex) println("String #" + i + " is " + x)
Scala 에는for
루프 구문 이 있다고 언급되었습니다 .
for (i <- 0 until xs.length) ...
또는 간단히
for (i <- xs.indices) ...
그러나 효율성도 요구했습니다. 그것은 스칼라 밝혀 for
구문은 실제로 같은 고차 방법에 대한 문법 설탕이다 map
, foreach
등 등과 같은, 어떤 경우에는 이러한 루프가 비효율적 일 수있다, 예를 들어 어떻게 최적화에 스칼라 및 루프 - 지능형 하시나요?
(좋은 소식은 Scala 팀이이 문제를 개선하기 위해 노력하고 있다는 것입니다. 다음은 버그 추적기의 문제입니다. https://issues.scala-lang.org/browse/SI-4633 )
최대한의 효율성을 위해 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"))
점을 유의 옵션 @tailrec
주석이 방법은 실제로 꼬리 재귀 있음을 보장하는 데 유용합니다. Scala 컴파일러는 tail-recursive 호출을 while 루프에 해당하는 바이트 코드로 변환합니다.
한 가지 더 방법 :
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
실제로 스칼라에는 인덱스가있는 오래된 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)
A는 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
스칼라에서 반복하는 것은 매우 간단합니다. 예를 들어 원하는 배열을 만듭니다.
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))
실제로 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
}
}
이것은 컬렉션에 대한 뷰를 만드는 것보다 조금 더 효율적이어야합니다.
stdlib에는 튜플 쓰레기를 생성하지 않고이를 수행 할 수있는 것이 없지만 직접 작성하는 것은 그리 어렵지 않습니다. 불행히도 나는 적절한 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 및 이와 유사한 map은 무언가를 반환합니다 (기능의 결과, 즉 println, Unit, 따라서 Unit 목록).
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)
carry-part는 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)
다음과 같은 접근 방식이 있습니다.
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);
}
}
A simple and efficient way, inspired from the implementation of transform
in SeqLike.scala
var i = 0
xs foreach { el =>
println("String #" + i + " is " + xs(i))
i += 1
}
The proposed solutions suffer from the fact that they either explicitly iterate over a collection or stuff the collection into a function. It is more natural to stick with the usual idioms of Scala and put the index inside the usual map- or foreach-methods. This can be done using memoizing. The resulting code might look like
myIterable map (doIndexed(someFunction))
Here is a way to achieve this purpose. Consider the following utility:
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)
}
}
This is already all you need. You can apply this for instance as follows:
import TraversableUtil._
List('a','b','c').map(doIndexed((i, char) => char + i))
which results in the list
List(97, 99, 101)
This way, you can use the usual Traversable-functions at the expense of wrapping your effective function. Enjoy!
참고URL : https://stackoverflow.com/questions/6833501/efficient-iteration-with-index-in-scala
'programing' 카테고리의 다른 글
Java에서 다차원 배열 초기화 (0) | 2020.10.13 |
---|---|
자바 : 파일 이름을 기본 및 확장으로 분할 (0) | 2020.10.13 |
Rails의 컨트롤러에서 레코드가 있는지 확인하십시오. (0) | 2020.10.13 |
Django : 인증 사용자를 모델로 가져 오기 (0) | 2020.10.13 |
Objective-C-nan에 대한 플로트 검사 (0) | 2020.10.13 |