KB 10 범위와 반복 — for 문에 세미콜론이 없다
고친 사람 github-actions[bot]
0. 첫 예제: for (int i = 0; i < n; i++) 가 없다
자바 개발자 손에 가장 익은 반복문은 아마 이것입니다.
public class Count {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
System.out.println(i);
}
}
}
0, 1, 2 를 한 줄씩 찍습니다. 이 for 줄을 코틀린 파일 Count.kt 의 main 안에 옮겨 코틀린 컴파일러 kotlinc 에 넘겼습니다. -d out 은 결과물을 out 폴더에 쓰라는 옵션입니다.
$ kotlinc Count.kt -d out
Count.kt:2:5: error: only expressions are allowed here.
for (int i = 0; i < 3; i++) {
^^^^^^^^^^^
...
Count.kt:2:14: error: syntax error: Expecting 'in'.
for (int i = 0; i < 3; i++) {
^
...
에러가 열두 개 나왔습니다. ... 로 줄인 두 곳에 열 개가 더 있습니다.
여기서 볼 것은 하나입니다. Count.kt:2:14 는 2번째 줄 14번째 칸이고, ^ 표시도 i 를 가리킵니다. 컴파일러는 int 를 반복 변수의 이름으로 읽었습니다. 그러고는 그 다음 칸(i 가 있는 곳)에 in 이 와야 한다고 했습니다. 코틀린의 for 에는 세미콜론 세 칸짜리 모양이 없습니다. for (변수 in 무엇) 한 가지 모양뿐입니다.
같은 일을 코틀린답게 쓰면 이렇습니다. Count2.kt 로 저장해 돌렸습니다.
fun main() {
for (i in 0 until 3) {
println(i)
}
}
$ kotlinc Count2.kt -d out
$ kotlin -cp out Count2Kt
0
1
2
fun main()— 프로그램 시작점인 함수입니다.fun은 함수를 선언하는 키워드입니다for (i in 0 until 3)—0 until 3이 만드는 0, 1, 2 를 차례로i에 넣으며 돕니다.i앞에int도val도 쓰지 않습니다println(i)— 한 줄 출력입니다. 줄 끝에 세미콜론이 없습니다kotlin— 코틀린 설치에 딸린 실행 명령입니다.java와 같은 일을 하되 코틀린 표준 라이브러리(kotlin.으로 시작하는 클래스가 든 jar)를 classpath 에 스스로 넣습니다Count2Kt— 클래스 밖에 쓴 함수를 담으려고 컴파일러가파일 이름 + Kt로 지은 클래스입니다
자바와 출력이 같습니다. 그러면 궁금한 것이 셋 생깁니다. 0 until 3 은 무엇인가. in 오른쪽에는 무엇이 올 수 있나. 이 반복이 자바의 인덱스 반복과 같은 코드로 도는가. 이 편은 그 셋을 차례로 풉니다. 출력은 전부 Kotlin 2.4.10, JDK(자바 개발 도구 묶음) 21 에서 직접 돌린 결과입니다.
1. .., ..<, until, downTo, step
0 until 3 같은 것을 코틀린에서는 범위(range)라고 부릅니다. 이 절은 범위를 만드는 다섯 가지 표기를 한 파일에 모아 돌리고, 자바의 for 와 맞대 봅니다. 코드에 쓰는 joinToString(" ") 은 범위에 든 수를 공백 하나씩 사이에 두고 이어 붙인 문자열을 돌려주는 함수입니다. 범위 안에 무엇이 들었는지 한 줄로 보려고 썼습니다.
fun main() {
// 1 2 3 4 5
println((1..5).joinToString(" "))
// 1 2 3 4
println((1..<5).joinToString(" "))
// 1 2 3 4
println((1 until 5).joinToString(" "))
// 5 4 3 2 1
println((5 downTo 1).joinToString(" "))
// 1 4 7 10
println((1..10 step 3).joinToString(" "))
// 10 6 2
println((10 downTo 1 step 4).joinToString(" "))
// (빈 줄)
println((5..1).joinToString(" "))
}
코드 줄이 길어 찍힌 값을 각 줄 위에 적었습니다.
$ kotlinc Ranges.kt -d out
$ kotlin -cp out RangesKt
1..5— 1 부터 5 까지, 양 끝을 모두 넣는 범위입니다1..<5와1 until 5— 끝값 5 를 빼는 범위입니다5 downTo 1— 5 에서 1 로 줄어드는 범위입니다step 3— 셋씩 건너뜁니다. 앞의 범위 뒤에 붙입니다
마지막 줄이 비었습니다. 5..1 은 거꾸로 세 주지 않고 빈 범위가 됐습니다. 줄어드는 범위는 반드시 downTo 로 씁니다.
자바 for 로 옮기면
다섯 표기를 자바 for 문의 괄호 안으로 옮기면 이렇습니다.
| 코틀린 | 자바 for 로 옮기면 |
끝값 |
|---|---|---|
1..5 |
for (int i = 1; i <= 5; i++) |
넣는다 |
1..<5 · 1 until 5 |
for (int i = 1; i < 5; i++) |
뺀다 |
5 downTo 1 |
for (int i = 5; i >= 1; i--) |
넣는다 |
1..10 step 3 |
for (int i = 1; i <= 10; i += 3) |
넣는다 |
until·downTo·step 은 키워드가 아니라 표준 라이브러리 함수입니다. 1.until(5) 를 점과 괄호 없이 1 until 5 로 부르게 해 주는 함수를 중위 함수(infix function)라고 부릅니다.
.. 와 ..< 은 연산자입니다. 공식 문서는 .. 이 rangeTo() 함수를, ..< 이 rangeUntil() 함수를 부른다고 소개합니다(코틀린 문서 「Ranges and progressions」).
자바 쪽은 표의 넷째 줄과 i -= 4 로 줄어드는 반복을 Ranges.java 로 돌렸습니다. 1 4 7 10 과 10 6 2 를 찍어 코틀린과 같았습니다.
반대도 마찬가지입니다. 0절에서 코틀린이 세미콜론 세 칸짜리 for 를 거절했듯, 자바는 범위 표기를 거절합니다. for (int i : 1..5) 라고 쓴 파일을 Bad.java 로 저장해 컴파일했습니다.
$ javac -d jout Bad.java
Bad.java:3: error: ')' expected
for (int i : 1..5) {
^
Bad.java:3: error: ';' expected
for (int i : 1..5) {
^
2 errors
범위는 값이다
1..9 step 3 을 반복에 쓰지 않고 변수에 담아 찍어 봤습니다.
fun main() {
val r = 1..9 step 3
println(r) // 1..7 step 3
println(r.first) // 1
println(r.last) // 7
println(5 downTo 1) // 5 downTo 1 step 1
println(3 in 1..5) // true
println(1..10 step 0) // 예외 — 아래
}
$ kotlinc Show.kt -d out
$ kotlin -cp out ShowKt
Exception in thread "main" java.lang.IllegalArgumentException: Step must be positive, was: 0.
...
val r = ...— 다시 대입할 수 없는 변수입니다. 타입을 안 적으면 값에서 알아냅니다r.first,r.last— 범위의 첫 값과 마지막 값입니다. 자바의getFirst()게터를 필드처럼 부르는 코틀린의 프로퍼티 문법입니다3 in 1..5— 3 이 범위 안에 드는지 묻는 식입니다.for의in과 글자는 같지만 여기서는 참·거짓을 돌려줍니다
9 까지라고 썼는데 1..7 step 3 이 찍혔습니다. 1 에서 셋씩 가면 1, 4, 7 다음이 10 이라 9 를 넘습니다. 그래서 만들 때 마지막 값을 7 로 미리 맞춰 둡니다. 공식 문서도 for (i in 1..9 step 3) 의 마지막 원소가 7 이라는 예를 들고, 「the last element is not always the same as the specified end value」라고 짚습니다(코틀린 문서 「Ranges and progressions」). 이 7 은 3절에서 컴파일 결과를 열 때 다시 나옵니다.
step 0 은 컴파일은 되고 실행할 때 IllegalArgumentException 으로 멈췄습니다. step 함수에는 양수만 넘깁니다.
그러면 줄어드는 방향은 무엇이 정할까요. downTo 가 속의 간격을 -1 로 두어 정합니다. 찍을 때만 5 downTo 1 step 1 처럼 양수로 보여 줍니다. 따로 (5 downTo 1).step 을 찍었더니 -1 이 나왔습니다.
찍힌 것을 모아 보면 범위가 무엇을 들고 있는지 드러납니다. r.first 와 r.last 로 첫 값과 마지막 값을 하나씩 읽었고, (5 downTo 1).step 으로 간격도 읽었습니다. 범위는 첫 값·마지막 값·간격 세 정수를 든 값입니다. 방향은 간격의 부호에 담기고, 마지막 값은 간격에 맞춰 미리 계산됩니다. 이 값이 클래스 파일에 객체로 남는 때와 남지 않는 때는 3절에서 가릅니다.
2. for in: 범위, 컬렉션, indices, withIndex
코틀린의 for 는 in 오른쪽에 범위만 받는 게 아닙니다. 자바의 향상된 for(for (String s : list))처럼 컬렉션도 받습니다. 이 절은 이름 세 개가 든 리스트를 세 가지로 돌고, 같은 일을 자바로 합니다.
fun main() {
val names = listOf("kim", "lee", "park")
for (name in names) {
println(name)
}
println(names.indices)
for (i in names.indices) {
println("$i ${names[i]}")
}
for ((i, name) in names.withIndex()) {
println("$i $name")
}
}
$ kotlinc Each.kt -d out
$ kotlin -cp out EachKt
kim
lee
park
0..2
0 kim
1 lee
2 park
0 kim
1 lee
2 park
listOf(...)— 읽기 전용 리스트를 만듭니다. JVM(java명령이 띄우는 자바 가상 머신)에서는java.util.List로 보입니다names.indices— 리스트에서 쓸 수 있는 인덱스의 범위입니다"$i ${names[i]}"—$i와${ }에 값이 끼워진 문자열입니다.names[i]는 자바의names.get(i)입니다names.withIndex()— 인덱스와 원소를 짝지어 돌려줍니다
println(names.indices) 가 0..2 를 찍었습니다. indices 는 1절에서 본 범위 그 자체입니다. 그래서 for (i in names.indices) 는 범위를 도는 for 와 같은 모양입니다. 마지막 for 는 withIndex 가 준 짝을 (i, name) 처럼 괄호로 받아 변수 둘로 풉니다. 이 문법을 구조 분해 선언(destructuring declaration)이라고 부릅니다.
자바로 같은 일을
자바는 앞의 두 반복을 이렇게 씁니다.
List<String> names = List.of("kim", "lee", "park");
for (String name : names) {
System.out.println(name);
}
for (int i = 0; i < names.size(); i++) {
System.out.println(i + " " + names.get(i));
}
Each.java 의 main 에서 이 부분만 옮겼습니다. 돌리면 kim·lee·park 과 0 kim·1 lee·2 park 를 코틀린과 같게 찍었습니다. withIndex 에 해당하는 것은 자바에 없습니다. 향상된 for 를 쓰면서 인덱스도 필요하면 바깥에 int i = 0 을 두고 직접 늘려야 합니다.
자바의 어떤 for 가 되나
이 반복들을 함수로 감싸 Loops.kt 에 모아 컴파일하고, 나온 클래스 파일을 디컴파일러 CFR 0.152 로 자바 코드로 되돌려 맞대 봤습니다. 디컴파일(decompile)은 클래스 파일을 거꾸로 읽어 소스 코드 모양으로 다시 적는 일입니다. 표의 IntArray 는 자바의 int[] 에 해당하는 코틀린 타입입니다.
| 코틀린 | CFR 결과 |
|---|---|
for (name in names) |
for (String name : names) |
IntArray 를 도는 for (x in xs) |
for (int x : xs) |
for (i in names.indices) |
size() 를 한 번 읽어 두고 도는 for (int i = 0; i < n; ++i) |
코틀린의 for 는 자바의 향상된 for 한 가지만 남긴 모양입니다. 인덱스가 필요하면 indices 라는 범위를 돌거나 withIndex 로 짝을 받습니다.
3. 범위를 도는 for 는 자바의 인덱스 루프와 같은가
1절에서 범위는 세 정수를 든 값이었습니다. 그렇다면 for (i in 0 until n) 을 돌 때마다 그 값을 담은 객체가 하나씩 생길까요. 자바의 for (int i = 0; i < n; i++) 는 정수 변수 하나만 늘리는 반복, 곧 인덱스 루프인데 말입니다. 이 절은 Loops.kt 에서 범위를 쓴 함수를 CFR 로 되돌려 봅니다.
끝이 변수인 1..n 은 while 이 됐다
범위를 쓴 톱레벨 함수(클래스 밖, 파일 바로 아래에 쓴 함수) 가운데 넷입니다. 인자는 n: Int 처럼 이름 뒤에 콜론과 타입을 쓰고, 본문이 한 문장이면 자바처럼 중괄호를 생략합니다. Int 는 자바의 int 입니다.
fun until(n: Int) {
for (i in 0 until n) println(i)
}
fun closed(n: Int) {
for (i in 1..n) println(i)
}
fun stepped(n: Int) {
for (i in 1..n step 2) println(i)
}
fun storedStep(n: Int) {
val r = 1..n step 2
for (i in r) println(i)
}
$ kotlinc Loops.kt -d out
$ java -jar cfr.jar out/LoopsKt.class
...
public final class LoopsKt {
public static final void until(int n) {
for (int i = 0; i < n; ++i) {
System.out.println(i);
}
}
public static final void closed(int n) {
int i = 1;
if (i <= n) {
while (true) {
System.out.println(i);
if (i == n) break;
++i;
}
}
}
...
맨 위의 머리 주석과 import, 한 줄이 아주 긴 @Metadata(...) 는 ... 로 줄였습니다. @Metadata 는 코틀린 컴파일러가 클래스에 남기는 메타데이터입니다.
until 은 자바에서 손으로 쓰는 for (int i = 0; i < n; ++i) 와 같습니다. 범위 객체는 어디에도 없습니다.
closed 는 모양이 다릅니다. 1..n 도 객체는 없지만, i <= n 을 조건으로 도는 대신 if (i == n) break 로 늘리기 전에 끝값과 견줍니다. 같은 일을 하는 자바 Loops.java 의 closed 는 조건이 i <= n 한 줄인 평범한 for 입니다.
끝이 3 같은 상수면 또 다릅니다. for (i in 1..3) 을 되돌리면 for (int i = 1; i < 4; ++i) 가 나왔습니다. 컴파일러가 <= 3 을 < 4 로 바꿔 쓴 것입니다.
| 끝값의 종류 | 컴파일된 모양 |
|---|---|
상수 3 |
for (int i = 1; i < 4; ++i) |
변수 n |
while (true) 안에서 if (i == n) break |
Int.MAX_VALUE |
while (true) 안에서 if (i == Integer.MAX_VALUE) break |
끝값의 종류가 반복의 모양을 가릅니다. 까닭은 4절에서 봅니다.
step 은 값만 미리 계산하고, 변수에 담으면 객체가 생긴다
같은 출력의 뒷부분, stepped 와 storedStep 입니다.
public static final void stepped(int n) {
int i = 1;
int n2 = ProgressionUtilKt.getProgressionLastElement((int)1, (int)n, (int)2);
if (i <= n2) {
while (true) {
System.out.println(i);
if (i == n2) break;
i += 2;
}
}
}
...
public static final void storedStep(int n) {
IntProgression r = RangesKt.step((IntProgression)((IntProgression)new IntRange(1, n)), (int)2);
int i = r.getFirst();
int n2 = r.getLast();
int n3 = r.getStep();
...
stepped— 객체가 없습니다. 표준 라이브러리 함수getProgressionLastElement로 마지막 값만 구하고, 나머지는closed와 같은 반복입니다. 1절에서1..9 step 3이 7 로 맞춰졌던 그 계산입니다storedStep—new IntRange(1, n)으로 객체가 생깁니다.RangesKt.step은 거기에 간격을 붙인IntProgression을 돌려줍니다. 반복은 그 객체의getFirst()·getLast()·getStep()을 읽고 돕니다. 간격의 부호로 방향이 갈리므로getStep()도 따로 읽습니다
나머지 둘도 같게 갈렸습니다. (1..n).reversed() 는 객체 없는 반복이 됐고, val r = 1..n 에 담아 돈 함수에는 new IntRange(1, n) 이 생겼습니다. 이 편에서 돌린 경우로는 in 오른쪽에 범위 식을 바로 썼는가가 객체를 갈랐습니다. 범위를 바로 쓰면 자바의 인덱스 루프처럼 객체 없는 정수 반복이 되고, step 과 reversed() 도 객체를 만들지 않았습니다.
4. 왜 i <= n 으로 안 도나
3절의 closed 는 i <= n 대신 i == n 에서 빠져나갔습니다. 차이는 끝값이 int 의 최댓값일 때 드러납니다. Edge.kt 에서 끝을 Int.MAX_VALUE(자바의 Integer.MAX_VALUE)로 둔 범위를 몇 번 도는지 셌습니다.
fun main() {
var count = 0
for (i in Int.MAX_VALUE - 2..Int.MAX_VALUE) count++
println(count) // 3
}
$ kotlin -cp out EdgeKt
자바 쪽은 같은 조건을 for 로 쓰고, 끝나지 않을 경우에 대비해 여섯 번째에 멈추게 했습니다.
int count = 0;
for (int i = Integer.MAX_VALUE - 2; i <= Integer.MAX_VALUE; i++) {
count++;
if (count > 5) break;
}
System.out.println(count); // 6
$ java -cp jout Edge
코틀린은 딱 세 번 돌았습니다. 자바는 break 에 걸려 6 이 찍혔습니다. 그 break 가 없었다면 끝나지 않습니다.
자바 쪽 — i 가 최댓값에서 1 늘면 int 가 넘쳐 가장 작은 음수가 됩니다. 이렇게 값이 타입의 범위를 벗어나 반대편으로 돌아가는 것을 오버플로라고 합니다. 그래서 i <= Integer.MAX_VALUE 는 영원히 참이고, 반복이 끝나지 않습니다.
코틀린 쪽 — 코틀린 컴파일러가 만든 코드는 늘리기 전에 i == 끝 을 봅니다. 그래서 넘칠 틈이 없습니다.
끝이 상수여도 그것이 Int.MAX_VALUE 면 3절의 1..3 처럼 < 끝 + 1 로 바꿔 쓸 수 없습니다. Int.MAX_VALUE + 1 은 int 에 없는 수라서입니다. 그래서 이 범위를 CFR 로 되돌리면 if (i == Integer.MAX_VALUE) break; 가 나왔습니다.
늘리기 전에 끝값과 견주므로 코틀린의 for 는 int 최댓값에서도 끝납니다.
5. while 과 do-while: 자바와 같아서 짧게
while 과 do-while 은 모양도 뜻도 자바와 같습니다. 이 절은 자바와 갈리는 두 가지만 봅니다. 하나는 do 본문의 변수를 조건에서 읽을 수 있는지, 다른 하나는 조건 안에서 대입할 수 있는지입니다.
do 본문의 변수가 조건에서 보인다
fun main() {
var n = 3
while (n > 0) {
print("$n ")
n--
}
println()
var tries = 0
do {
tries++
val ok = tries >= 2
} while (!ok)
println(tries)
}
$ kotlinc Retry.kt -d out
$ kotlin -cp out RetryKt
3 2 1
2
$ javac -d jout Retry.java
Retry.java:7: error: cannot find symbol
} while (!ok);
^
symbol: variable ok
location: class Retry
1 error
var n = 3— 다시 대입할 수 있는 변수입니다.n--는 자바와 같습니다print— 줄바꿈 없이 출력합니다val ok는do { }안에서 선언했는데 바깥while (!ok)에서 읽습니다
뒤이어 돌린 자바 Retry.java 도 do { } 안에 boolean ok = tries >= 2; 를 두고 } while (!ok); 로 닫은 것입니다. 자바에서 ok 는 중괄호가 닫히면 사라지는 변수라 조건에서 못 찾았습니다. 코틀린은 do 본문에서 선언한 변수를 while 조건까지 보여 줍니다. 재시도처럼 「해 보고 결과를 조건으로 삼는」 반복에서 바깥 변수를 하나 줄여 줍니다.
조건 안에서 대입할 수 없다
자바에서 줄 단위로 읽을 때 흔히 쓰는 while ((line = reader.readLine()) != null) 을 코틀린 Read.kt 로 옮겼습니다.
$ kotlinc Read.kt -d out
Read.kt:4:13: error: only expressions are allowed in this context.
while ((line = reader.readLine()) != null) {
^^^^^^^^^^^^^^^^^^^^^^^^
코틀린에서 대입은 값을 내는 식이 아니라서 조건 안에 못 씁니다. 대신 이렇게 씁니다.
fun main() {
val reader = System.`in`.bufferedReader()
while (true) {
val line = reader.readLine() ?: break
println(line)
}
}
System.`in`— 자바의System.in입니다.in이 코틀린 키워드라 백틱으로 감쌌습니다bufferedReader()— 입력 스트림을 줄 단위로 읽을 수 있는BufferedReader로 감싸 주는 표준 라이브러리 함수입니다?:— 왼쪽 값이null이면 오른쪽을 실행하는 연산자입니다. 여기서는 더 읽을 줄이 없으면break로 반복을 빠져나갑니다
Read2.kt 로 저장해 printf 'a\nb\n' 을 입력으로 넣었더니 a, b 를 한 줄씩 찍었습니다. 자바 Read.java 도 같은 입력에 같게 찍었습니다.
정리하면, while 과 do-while 은 자바와 같습니다. 다른 점은 do 본문의 변수를 조건에서 읽을 수 있다는 것과, 조건 안에서 대입 대신 ?: break 를 쓴다는 것입니다.
6. break·continue 와 라벨 break@outer
안쪽 반복에서 바깥 반복까지 한 번에 빠져나가려면 자바는 라벨 outer: 를 붙이고 break outer; 를 씁니다. 코틀린은 자바의 콜론 대신 @ 를 씁니다. 선언은 이름 뒤에 붙여 outer@, 부를 때는 이름 앞에 붙여 break@outer 입니다. 이 절은 2중 반복에서 continue 와 break 로 바깥 반복을 건드리는 코드를 두 언어로 돌려 맞대 봅니다.
fun main() {
outer@ for (i in 1..3) {
for (j in 1..3) {
if (j == 2) continue@outer
if (i == 3) break@outer
println("$i $j")
}
}
println("end")
}
outer@— 라벨 선언입니다. 이름 뒤에@를 붙여 반복문 앞에 둡니다continue@outer— 안쪽 반복을 버리고 바깥 반복의 다음 차례로 갑니다break@outer— 바깥 반복까지 끝내고 그 뒤의println("end")로 갑니다
같은 일을 하는 자바 Find.java 입니다.
outer:
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
if (j == 2) continue outer;
if (i == 3) break outer;
System.out.println(i + " " + j);
}
}
System.out.println("end");
두 프로그램 모두 1 1, 2 1, end 를 찍었습니다. i 가 1 과 2 일 때는 j 가 2 에 닿자마자 바깥으로 넘어가 한 줄씩만 찍었습니다. i 가 3 이 되자 첫 j 에서 전부 끝났습니다. 두 언어가 같습니다.
forEach 안의 break
break 가 거절된 경우도 둘 봤습니다.
Lambda.kt— 반복문 없이listOf(1, 2, 3).forEach { }의 중괄호 안에if (it == 2) break를 썼습니다.forEach { }는 리스트의 원소마다 중괄호 안의 코드를 실행하는 함수이고,it은 그 원소입니다. 이break는'break' and 'continue' are only allowed inside loops.로 거절됐습니다NoLabel.kt— 선언하지 않은 라벨로break@outer를 쓰면label does not denote a reachable loop.가 났습니다
중괄호 안이라서 거절된 것은 아닙니다. 같은 forEach 를 for (x in 1..3) { } 안에 넣고 println("$x $it") 을 붙인 NonLocal.kt 는 컴파일이 됐습니다. 돌리면 1 1 만 찍고 곧장 end 로 갔습니다. x 가 1 일 때 만난 break 가 바깥 for 를 끝낸 것입니다.
이 break 는 바깥 for 를 끝냅니다. 중괄호 밖의 반복까지 끝내는 이 동작은 Kotlin 2.2 에서 안정 기능(Stable)이 됐습니다(코틀린 문서 「What's new in Kotlin 2.2.0」).
정리하면, 라벨 반복은 @ 표기만 다르고 자바와 같게 돕니다. 반복문 밖에서 forEach 에 넘긴 중괄호 안의 break 는 거절되고, 바깥에 for 가 있으면 그 break 는 바깥 for 를 끝냅니다.
7. 한 장 요약
코틀린의
for는for (변수 in 무엇)한 가지뿐이다. 세미콜론 세 칸 대신..·..<·until·downTo·step으로 만든 범위를 넘긴다.in오른쪽에 범위를 바로 쓰면 객체 없는 정수 반복으로 컴파일된다. 끝이 변수인1..n은i == n에서 늘리기 전에 빠져나가int최댓값에서도 넘치지 않는다.
| 하려는 일 | 자바 | 코틀린 | 확인한 방법 |
|---|---|---|---|
| 0 부터 n 앞까지 | for (int i = 0; i < n; i++) |
for (i in 0 until n) · 0..<n |
CFR 에서 같은 for |
| 1 부터 n 까지 | for (int i = 1; i <= n; i++) |
for (i in 1..n) |
끝이 변수일 때 CFR 에서 while + if (i == n) break. 끝이 3 이면 i < 4 |
끝이 MAX_VALUE |
오버플로로 안 끝남 | 3 번 돌고 끝남 | Edge 실행 |
| 줄어드는 반복 | i-- |
n downTo 1 · (1..n).reversed(). 5..1 은 빈 범위 |
실행 · CFR |
| 간격 | i += 3 |
step 3. 마지막 값은 1..7 step 3 처럼 맞춰짐. step 0 은 예외. downTo 의 간격은 -1 |
Show.kt |
| 범위를 변수에 담기 | 없음 | IntRange·IntProgression 객체가 생김 |
CFR 의 new IntRange |
| 컬렉션 | for (String s : list) |
for (s in list) |
CFR |
| 인덱스와 원소 | 바깥 카운터 | indices · withIndex() |
CFR |
do 본문 변수를 조건에서 |
cannot find symbol |
된다 | Retry |
| 조건 안 대입 | (line = r.readLine()) != null |
에러. ?: break 로 씀 |
Read.kt |
| 라벨 | outer: · break outer; |
outer@ · break@outer |
Find 실행 |
forEach { } 안 break |
해당 없음 | 반복문 밖이면 에러, for 안이면 그 for 를 끝냄 |
Lambda.kt · NonLocal.kt |
관련 항목
코틀린에서 반복을 쓰는 문법 요소
for 문 · while 문 · do-while 문 · break 문 · continue 문 · 라벨 · 향상된 for 문
for 문의 in 오른쪽에 오는 반복 대상
범위 · IntRange · IntProgression · 컬렉션 · 배열 · 이터레이터 · Iterable
범위를 만드는 코틀린 연산자와 함수
rangeTo · rangeUntil · until · downTo · step · reversed · 중위 함수
인덱스와 원소를 함께 꺼내는 수단
indices · withIndex · 구조 분해 선언 · 문자열 템플릿
범위와 반복이 부딪히는 경계 값과 예외
정수 오버플로 · Integer.MAX_VALUE · 빈 범위 · IllegalArgumentException
반복문 안에서 코틀린과 자바가 갈리는 문법
대입식 · 엘비스 연산자 · 변수 스코프 · 람다 · forEach
반복문이 클래스 파일이 되어 실행되기까지 거치는 처리 단계
kotlinc · javac · 컴파일러 · 클래스 파일 · JVM · 가상 머신 · 코틀린 표준 라이브러리
클래스 파일을 열어 보는 도구
javap · 디컴파일 · 디컴파일러 · CFR · 메타데이터