본문 바로가기

Dev Book/Kotlin IN ACTION

CH5(5.3). 지연 계산 컬렉션 연산

1. 지연 계산 컬렉션 연산

- map이나 filter 같은 함수들은 결과 컬렉션을 즉시 생성한다. 이는 컬렉션 함수를 연쇄하면 매 단계마다 계산 중간 결과를 새로운 컬렉션에 임시도 담는다는 뜻이다. 시퀀스를 사용하면 중간 임시 컬렉션을 사용하지 않고도 컬렉션 연산을 연쇄할 수 있다.

 

people.map(Person::name).filter{ it.startWith("A") }

 

-코틀린 표준 라이브러리 참조 문서에는 filter와 map이 리스트를 반환한다고 써 있다. 이는 이 연쇄 호출이 리스트를 2개 만든다는 뜻이다. 한 리스트는 filter의 결과를 담고, 다른 하나는 map의 결과를 담는다. 만약 원소가 수백만개가 되면 효율이 떨어질 것이다. 이를 효율적으로 하기 위해서는 각 연산이 컬렉션을 직접 사용하는 대신 시퀀스를 사용하게 만들어야 한다.

 

people.asSequence().map(Person::name).filter { it.startWith("A") }.toList()

 

- 코틀린 지연 계산 시퀀스는 Sequence 인터페이스에서 시작한다. 이 인터페이스는 단지 한 번에 하나씩 열거될 수 있는 원소의 시퀀스를 표현할 뿐이다. Sequence 안에는 iterator라는 단 하나의 메소드가 있고, 그 메소드를 통해 시퀀스로부터 원소 값을 얻을 수 있다.

 

- asSequence 확장 함수를 호출하면 어떤 컬렉션이든 시퀀스로 바꿀 수 있다. 시퀀스를 리스트로 만들 때는 toList를 사용한다.

 

2. 시퀀스 연산 실행

- 시퀀스에 대한 연산은 중간 연산과 최종 연산으로 나뉜다. 중간 연산은 다른 시퀀스를 반환하며, 최종 연산은 결과를 반환한다. 중간 연산은 항상 지연 계산된다. 

 

>>> listOf(1,2,3,4).asSequence().map{ print(" map($it) "); it*it }
    	    	 .filter{ print("filter($it) "); it%2==0 }

 

최종 연산이 없는 위 코드는 아무 내용도 출력되지 않는다. 이는 map과 filter 변환이 늦춰져서 결과를 얻을 필요가 있을 때(최종 연산이 호출 될 때) 적용된다는 뜻이다.

 

- 시퀀스의 경우 모든 연산은 각 원소에 대해 순차적으로 적용된다. 첫번째 원소가 처리되고, 다시 두번째 원소가 처리되며, 이런 처리가 모든 원소에 대해 적용된다. 따라서 원소에 연산을 차례대로 적용하다가 결과가 얻어지면 그 이후의 원소에 대해서는 변환이 이뤄지지 않을 수도 있다.

 

3. 시퀀스 만들기

- generateSequence 함수를 사용해 시퀀스를 만들 수 있다. 이 함수는 이전의 원소를 인자로 받아 다음 원소를 계산한다.

 

>>> val naturalNumbers = generateSequence(0) { it+1 }
>>> val numbersTo100 = naturalNumbers.takeWhile { it<=100 }
>>>println(numbersTo100.sum())
5050

 

이 예제에서 naturalNumbers와 numbersTo100은 모두 시퀀스이며, 연산을 지연 계산한다. 최종 연산을 수행하기 전까지는 시퀀스의 각 숫자는 계산되지 않는다.