5. [3] 지연 계산(lazy) 컬렉션 연산

people.asSequence()                     // 원본 컬렉션을 시퀀스로 변환
      .map(Person::name)
      .filter { it.startsWith("A") }    // 시퀀스도 컬렉션과 동일한 API 제공
      .toList()                         // 결과 시퀀스를 다시 리스트로 변환
  • map이나 filter같은 컬렉션 함수들은 결과 컬렉션을 즉시 생성한다.
  • 이는 컬렉션 함수를 연쇄하면 매 단계 마다 계산 중간 결과를 새로운 컬렉션에 담는다는 의미이다.
  • 연산을 효율적으로 사용하기 위해선 각 연산이 컬렉션을 직접 사용하는 대신 시퀀스를 사용하게 만들어야 한다.

[Sequence]

  • 해당 시퀀스를 사용 시 중간 임시 컬렉션을 사용하지 않고 컬렉션 연산을 연쇄하는 것이 가능하다.
  • Kotlin의 지연 계산 시퀀스는 Sequence 인터페이스에서 시작한다.
  • Sequence 안에는 iterator이라는 메서드를 통해 시퀀스로부터 원소 값을 얻을 수 있다.
  • 그 인터페이스 위에 구현된 연산이 계산을 수행하는 방법에서 장점이 생긴다.
  • 중간 처리 결과를 저장하지 않고도 연산을 연쇄적으로 적용해서 효율적으로 계산을 수행할 수 있다.
  • asSequence 확장 함수 호출 시 어떤 컬렉션이든 시퀀스로 변경할 수 있으며, 이를 리스트로 만들 떄는 toList를 활용한다.
  • 시퀀스에 대한 연산을 지연 계산 하기 때문에 실행하게 만드려면 시퀀스의 원소를 하나씩 이터레이션하거나, 리스트로 변환해야 한다.

(1) 시퀀스 연산 실행: 중간 연산과 최종 연산

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

// map(1) filter(1) map(2) filter(4) map(3) filter(9) map(4) filter(16)
println(listOf(1, 2, 3, 4).asSequence()
                          .map {it * it}.find { it > 3 })
// 4
  • 시퀀스에 대한 연산은 중간 연산과 최종 연산으로 나뉜다.
  • 중간 연산은 항상 지연계산 되며, 이는 다른 시퀀스를 반환한다.
  • 직접 연산의 경우 map을 먼저 수행하여 시퀀스를 얻고, 그 시퀀스에 대해 filter를 수행할 것이다.
  • 시퀀스의 경우 모든 연산은 각 원소에 대해 순차적으로 적용된다. 즉, 첫 번째 원소가 처리 된 후, 두 번째 원소가 처리된다.
  • 원소에 연산을 차례로 적용하다가 결과가 얻어지면 그 이후의 원소에 대해서는 변환이 이루어지지 않을 수 있다.
  • 위 예제에서 같은 연산을 시퀀스가 아니라 컬렉션에 수행하면, map이 먼저 평가되어 최초 컬렉션의 모든 원소가 변환된다.
  • 하지만 시퀀스에 수행했기 때문에, 원소 중 일부의 계산은 이루어지지 않는다.

(2) 시퀀스 만들기

val naturalNumbers = generateSequence(0) { it + 1 }
val numbersTo100 = naturalNumbers.takeWhile { it <= 100 }
println(numbersTo100.sum())   // 모든 지연 연산은 'sum'연산을 계산할 때 수행한다.
// 5050
fun File.isInsideHiddenDirectory() = generateSequence(this){it.parentFile}.any {it.hidden}
val file = File("/Users/svtk/.HiddenDir/a.txt")
println(file.isInsideHiddenDirectory())   // true
  • generateSequence 함수를 통해서도 시퀀스를 만드는 것이 가능하다.
  • 이 함수는 이전의 원소를 인자로 받아서 다음 원소를 계산한다.
  • 위 예제에서 naturalNumbers와 numbersTo100은 모두 시퀀스이며, 연산을 지연 계산한다.
  • 최종 연산을 수행하기 전까지는 시퀀스의 각 숫자는 계산되지 않는다.
  • 객체의 조상으로 이루어진 시퀀스를 만들어내야 한다. 어떤 객체의 조상이 같은 타입이고, 모든 조상의 시퀀스에서 특성을 알고 싶을 때가 있다.
  • 여기서도 첫 번쨰 원소를 지정하고, 시퀀스의 한 원소로부터 다음 원소를 계산하는 방법을 제공함으로써 시퀀스를 만든다.