Skip to content

Commit

Permalink
11월 10일 TIL
Browse files Browse the repository at this point in the history
  • Loading branch information
coding-polarbear committed Nov 10, 2021
1 parent 7a49be2 commit 25de9d9
Show file tree
Hide file tree
Showing 2 changed files with 290 additions and 0 deletions.
66 changes: 66 additions & 0 deletions 알고리즘/plus_one.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Plus One

You are given a **large integer** represented as an integer array `digits`, where each `digits[i]` is the `ith` digit of the integer. The digits are ordered from most significant to least significant in left-to-right order. The large integer does not contain any leading `0`'s.

Increment the large integer by one and return the resulting array of digits.

## Example 1:
```
Input: digits = [1,2,3]
Output: [1,2,4]
Explanation: The array represents the integer 123.
Incrementing by one gives 123 + 1 = 124.
Thus, the result should be [1,2,4].
```

## Example 2:
```
Input: digits = [4,3,2,1]
Output: [4,3,2,2]
Explanation: The array represents the integer 4321.
Incrementing by one gives 4321 + 1 = 4322.
Thus, the result should be [4,3,2,2].
```

## Example 3:
```
Input: digits = [0]
Output: [1]
Explanation: The array represents the integer 0.
Incrementing by one gives 0 + 1 = 1.
Thus, the result should be [1].
```

## Example 4:
```
Input: digits = [9]
Output: [1,0]
Explanation: The array represents the integer 9.
Incrementing by one gives 9 + 1 = 10.
Thus, the result should be [1,0].
```

## 풀이
```kotlin
class Solution {
fun plusOne(digits: IntArray): IntArray {
var carry = 1
var index = digits.size - 1
var result: IntArray = digits

while(index >= 0 && carry > 0) {
result[index] = (result[index] + 1) % 10
carry = if(result[index] == 0) 1 else 0

index--
}

if(carry > 0) {
result = IntArray(digits.size + 1)
result[0] = 1
}

return result
}
}
```
224 changes: 224 additions & 0 deletions 코루틴/코루틴 공식가이드2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
# 코틀린 공식 가이드 2

## 취소(Cancellation)
* 코루틴에서 실행되는 모든 중단 함수 (`suspending function`)들은 취소 요청에 응답 가능하도록 구현되어야 함.
* 중단 함수는 실행 중 취소 가능한 구간마다 취소 요청이 있었는지 확인하고 요청이 있었다면 실행을 즉시 취소하도록 구현해야 함.
* `kotlinx.coroutines` 라이브러리의 모든 중단함수는 이러한 취소 요청에 대응하도록 구현되어있음.
* 취소를 지원하는 중단 함수들은 실행하는 동안 취소가 가능한 지점마다 현재 코루틴이 취소 되었는지 확인하며, 만약 취소되었다면 `CancellationException`을 발생시키며 종료함.

> ReactiveX Observable을 구현해 본 경험이 있는 사람이라면 Observable의 코드 실행 중 취소 가능한 구간마다 isDisposed() 같은 취소 상태 확인 함수를 이용하여 현재 Observable의 구독 취소 여부를 확인하고 Observable의 데이터 방출을 정지하고 종료해야 하는지 판단해본 기억이 있을 것입니다.
* 만약 다음과 같이 코루틴을 작성하면 취소 요청이 오더라도 작업을 멈추지 않고 계속 진행함 (`Sleep` 대신 `Busy-waiting` 구현을 해도 마찬가지)

```kotlin
fun main(args: Array<String>) = runBlocking {
val job = launch(Dispatchers.Default) {
for(i in 1..10) {
println("I'm sleeping $i ...")
Thread.sleep(500L)
}
}

delay(1300L)
println("main: I'm tired of waiting!")
job.cancelAndJoin()
println("main: Now I can quit.")
}
```

* 이 코루틴을 취소 요청에 친화적인 코드로 만들기 위해서는 취소가 가능한 시점마다 다른 `Continuation`에 실행 시간을 양보하는 `yield()` 함수를 호출하거나
* `CoroutineScope`에 정의된 `isActive` 속성을 참조하여 코루틴이 비활성 상태인 경우 작업을 중단하도록 하는 방법이 있음.

### yield() 중단함수를 사용한 구현

```kotlin
fun main(args: Array<String>) = runBlocking {
val job = launch(Dispatchers.Default) {
for(i in 1..10) {
yield()
println("I'm sleeping $i ...")
Thread.sleep(500L)
}
}

delay(1300L)
println("main: I'm tired of waiting!")
job.cancelAndJoin()
println("main: Now I can quit.")
}
```

### isActive를 이용한 구현

```kotlin
fun main(args: Array<String>) = runBlocking {
val job = launch(Dispatchers.Default) {
for(i in 1..10) {
if(!isActive) {
break
}
println("I'm sleeping $i ...")
Thread.sleep(500L)
}
}

delay(1300L)
println("main: I'm tired of waiting!")
job.cancelAndJoin()
println("main: Now I can quit.")
}
```

* 취소가 가능한 중단함수들은 췻되면 `CancellationException`을 발생시킴
* 우리는 일반적인 예외처리 방식과 동일하게 이를 처리할 수 있음.
* 만약 예외발생시 해제 해야할 리소스가 있다면
* `try~finally` 구문을 사용
* `use()` 함수를 사용
* 두 가지 방법으로 해결할 수 있음.

### try ~ finally를 사용한 구현
```kotlin
fun main(args: Array<String>) = runBlocking {
val job = launch(Dispatchers.Default) {
try {
repeat(1000) { i->
println("I'm sleeping $i ...")
delay(500L)
}
} finally {
println("main : I'm running finally!")
}
}

delay(1300L)
println("main: I'm tired of waiting!")
job.cancelAndJoin()
println("main: Now I can quit.")
}
```

### use() 메소드를 이용한 구현
```kotlin
fun main(args: Array<String>) = runBlocking {
val job = launch {
SleepingBed().use {
it.sleep(1000)
}
}

delay(1300L)
println("main : I'm tired of waiting!")
job.cancelAndJoin()
println("main : Now I can quit.")
}

class SleepingBed : Closeable {

suspend fun sleep(times: Int) {
repeat(times) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
}

override fun close() {
println("main : I'm running close() in SleepingBed!")
}

}
```

## 취소 불가능한 코드 블럭의 실행 (Run non-cancellable block)
* 이미 `CancellableException`이 발생한 코루틴의 `finally` 블록 안에서 중단 함수를 호출하면 현재 코루틴은 이미 취소된 상태이기 때문에 `CancellationException`이 발생함.
* 보통 리소스를 정리하는 함수들은 논-블록킹으로 동작하기 때문에 이러한 제약이 큰 문제가 되지는 않음.
* 이미 취소된 코루틴 안에서 동기적으로 어떤 중단 함수를 호출해야 하는 상황이라면 `withContext{ }` 코루틴 빌더에 `NonCanecllable` 컨텍스트를 전달하여 이를 처리할 수 있음.

```kotlin
fun main(args: Array<String>) = runBlocking {
val job = launch {
try {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
} finally {
withContext(NonCancellable) {
delay(1000)
println("main : I'm running finally!")
}
}
}

delay(1300L)
println("main : I'm tired of waiting!")
job.cancelAndJoin()
println("main : Now I can quit.")
}
```


## 타임아웃 (Timeout)
* 일반적으로 어떤 코루틴의 실행을 중간에 취소해야 하는 경우는 그 수행시간이 너무 길어져 허용할 수 있는 시간을 넘어섰을 경우
* 이 경우 우리는 타임아웃을 지정하고 이 시간을 넘어설 경우 해당 작업을 취소하도록 구현할 수 있음.
* 일반적으로 취소 요청이 없었음에도 어떤 코루틴의 실행을 중간에 취소해야 하는 경우는 그 코루틴의 수행시간이 허용 가능한 시간보다 길어졌을 경우.
* 이러한 경우를 다루기 위해서 우리는 코루틴에 제한 시간 (`Timeout`)을 설정하고 이 시간이 넘어설 경우 코루틴을 취소되도록 구현할 수 있음.

1. 제한 시간을 설정 할 대상이 되는 코루틴을 생성.
2. 일정 시간 (Timeout) 지연 후 전달받은 `job`이 끝나지 않았으면 취소하는 동작을 하는 코루틴을 생성하고, 1번에서 만들고 실행한 코루틴의 `job` 객체를 전달함.
3. 테스트를 위해 1번 코루틴은 2번 코루틴에서 설정한 시간보다 긴 수행시간을 갖도록 구현함.

```kotlin
fun main(args: Array<String>) = runBlocking<Unit> {
val job = launch {
try {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
} finally {
println("main : I'm running finally!")
}
}

launch {
delay(1300L)
println("main : I'm tired of waiting. Cancel the job!")
if (job.isActive) {
job.cancelAndJoin()
}
}
}
```

* 위 구현은 우리가 예상한대로 동작함.
* 하지만 매번 Job 객체 참조를 유지하면서 별도의 코루틴에서 취소 하도록 하는 것은 번거롭기 때문에 이 경우 보통 `withTimeout()` 함수를 사용함.

```kotlin
fun main(args: Array<String>) = runBlocking<Unit> {
withTimeout(1300L) {
launch {
try {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
} finally {
println("main : I'm running finally")
}
}
}
}
```

* 위 함수를 실행하면 `TimeoutCancellationException`이 발생
* 이는 예제가 메인 함수에서 바로 실행되었기 때문.
* 코루틴이 취소될 경우 발생하는 `CancellationException`은 코루틴에서 일반적인 종료 상항 중 하나로 간주됨.

```
I’m sleeping 0 …
I’m sleeping 1 …
I’m sleeping 2 …
main : I’m running finally!
Exception in thread “main” kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 1300 ms at kotlinx.coroutines.TimeoutKt.TimeoutCancellationException(Timeout.kt:122) at kotlinx.coroutines.TimeoutCoroutine.run(Timeout.kt:88)
Process finished with exit code 1
```

0 comments on commit 25de9d9

Please sign in to comment.