변수의 스코프를 최소화 해라
만약 loop 안에서만 변수가 쓰인다면 loop 안으로 변수를 넣어서 scope 를 최소화 해라.
보통의 언어 에서 변수의 scope 는 보통 curly braces 안으로 생성지만, Kotlin 에서는 외부의 변수에도 접근 가능하다.
음, 내가 생각해도 뭔가 잘 읽기 힘들게 적은거 같아 코드로 적어보려고 한다.
val a = 1
fun fizz() {
val b = 2
print(a+b)
}
val buzz = {
val c = 3
println(a + c)
}
위의 예시를 보면, buzz 와 fizz 의 curly braces 지역 scope 에서 외부 변수인 a
에 접근할 수 있음을 알 수 있다.
반대로 모두 알고 있겠지만 Outer area 에서 inner function scope 의 variable 에 접근은 불가능하다. 가시성을 제한하는 예시를 한번 알아보자.
val user: User
for (i in users.indices) {
user = users[i]
println("User at $i is $user")
}
위의 예시를 보면 user 는 for function scope 안에서만 이용하므로 for scope 안으로 변수 scope 를 제한시켜야 한다.
따라서 아래와 같이 코드를 작성하는 것이 좀 더 좋은 방향일 것 이다.
for ((i, user) in users.withIndex()) {
println("User at $i is $user")
}
위와 같이 코드를 작성했을때 user 는 for loop 의 scope 로 제한되므로 outside 에서 접근하는 것이 불가능 해진다.
이렇게 변수의 scope 를 tight 하게 제한했을때, 우리는 프로그램을 좀더 쉽게 추적할 수 있고, 관리할 수 있다고 한다.
이렇게 변수의 scope 를 제한하는것 또한 property 가 어느 지점에서 수정되는지를 제한할 수 있고, 추적/관리하기 쉬우므로 immutable properties 로 변수를 제한하는것과 비슷한 의미이다. 아래 코드를 한번 보자
val user: User
if (hasValue) {
user = getValue()
} else {
user = User()
}
위의 코드를 어떻게 scope 를 제한할 수 있을까?
val user: User = if (hasValue) {
getValue()
} else {
User()
}
위와 같이 적는것이 value 가 필요한 시점이 Initialize 를 하는 것이므로 좀 더 좋은 코드일 것이다.
우리가 한번에 두개 이상의 properties 를 update 해야 한다면 destructuring 을 이용할 수도 있다.
fun updateWeather(degress: Int) {
val description: String
val color: Int
if (degress < 5) {
description = "cold"
color = Color.BLUE
} else if (degress < 23) {
description = "mild"
color = Color.YELLOW
} else {
description = "hot"
color = Color.RED
}
}
fun updateWeather(degress: Int) {
val (description, color) = when {
degress < 5 -> "cold" to Color.BLUE
degress < 23 -> "mild" to Color.YELLOW
else -> "hot" to Color.RED
}
}
destructuring 과 Pair 를 통해서 multiplation properties 를 깔끔하게 update 를 할 수 있다는게 좋은것 같다. 좀 더 Kotlin 스러운 느낌
Capturing
저자의 Kotlin Coroutines 를 이용해 에라토스테네스의 체를 구하는 로직을 한번 보자.
val numbers = (2..100).toList()
val primes = mutableListOf<Int>()
while (numbers.isNotEmpty() {
val prime = numbers.first()
primes.add(prime)
numbers = number.filter { it % prime != 0 }
})
val primes: Sequence<Int> = sequence {
val numbers = generateSequence(2) {it + 1}
while (true) {
val prime = numbers.first()
yield(prime)
numbers = numbers.drop(1).filter( it % prime != 0 )
}
}
print(primes.take(10).toList()) [2, 3, 5, 7, 11, 13, 17, 19, 23 ,29]
Sequence 를 이용해서 에라토스테네스체를 optimize 한 과정이다. 아래 코드는 위와 비슷해보이지만 제대로 동작하지 않는다.
val primes: Sequence<Int> = sequence {
val numbers = generateSequence(2) {it + 1}
var prime: Int
while (true) {
prime = numbers.first()
yield(prime)
numbers = numbers.drop(1).filter( it % prime != 0 )
}
}
print(primes.take(10).toList()) [2, 3, 5, 6, 7, 8, 9, 10, 11, 12]
왜 var prime 을 while loop 위로 올렸다고 다르게 동작할까? 대부분의 사람들은 같게 동작 한다고 생각할 것이다.
sequence 는 기본적으로 executed lazy 한 속성을 지니고 있다. 즉, filter 가 작동되기도 전에 prime 의 값이 바뀌어서 저장될 수 있다는 뜻이다. 따라서 이런 문제가 있을 수 있기때문에 scope 를 한정짓고, immutable 하게 property 를 이용하라는 것이다.
'Kotlin' 카테고리의 다른 글
코루틴으로 Heap 사용량 최적화(?) 한 이야기 끄적 (0) | 2022.06.02 |
---|---|
Kotlin Coroutines - Basic (0) | 2022.05.29 |
Effective Kotlin - Item01 (0) | 2022.05.25 |
GC 분석해보기 (0) | 2022.05.24 |
Kotlin Delegation (0) | 2022.05.16 |