티스토리 뷰

🍏/Swift

[Swift] LazySequence

eungding 2022. 5. 25. 09:22
반응형

[1] 용어정리 (출처: 이펙티브 코틀린) 

애플 문서(Sequence and Collection)에  'eager'  이라는 단어가 나오는 데 이것을 이해하기 위해

이펙티브 코틀린 책내용을 가져왔습니다!

 


 

✔️ eager order (step-by-step)

- 요소 전체를 대상으로 연산을 차근차근 적용한다. 

- 각 단계에서 연산이 이루어져 새로운 컬렉션을 만들어낸다.

 

 

 

✔️ lazy order (element-by-element)

- 요소 하나하나에 지정한 연산을 한꺼번에 적용한다. 

- 최종 연산이 이루어지기 전까지는 각 단계에서 연산이 일어나지 않는다. 

 

 

==>

사전적 의미로 lazy = 게으른, eager = 열렬한(열심인) 이기 때문에 

저 두단어가 같이 쓰이는 것 같아요! 

 

 

[ eager vs lazy ]

 

1.

컬렉션 처리 함수를 사용하지 않고, 고전적인 반복문과 조건문을 활용해서 코드를 구현한다면 이는 lazy order와 같다. 

그래서 lazy가 훨씬 자연스러운 처리라고 할 수 있다. 

 

2. 

eager 은 각각의 단계에서 만들어진 결과를 활용하거나 저장할 수 있다는 장점이 있지만,

각각의 단계에서 결과가 만들어지면서 공간을 차지하는 비용이 든다는 단점도 있다. 

 

크거나 무거운 컬렉션을 처리할 때, 메모리 낭비를 줄이고 싶은 경우

lazy를 활용하면 좋다. 

 

3.

중간처리 단계를 모든 요소에 적용할 필요가 없다면

최소 연산만 할 수 있도록 lazy를 활용하면 좋다.

 

 

==>  한줄 요약: 

lazy는 불필요한 공간 할당 및 계산을  피하는 데 사용될 수 있으며 eager보다 더 자연스러운 처리순서를 가진다. 

 


 

[2] LazySequenceProtocol  / LazySequence 살펴보기 

LazySequenceProtocol  은 lazy 라는 property를 가지고 있습니다. 

이를 통해 LazySequence (lazy order 로 동작하는 시퀀스)를 얻을 수 있습니다. 

 

 

https://developer.apple.com/documentation/swift/lazysequenceprotocol

 

Relationships도 참고해주세요! 

 

# LazySequenceProtocol 

 

https://developer.apple.com/documentation/swift/lazysequenceprotocol

 

# LazySequence

 

LazySequence 는 struct 이고,  LazySequenceProtocol  와 Sequence 를 컨펌! 

 

https://developer.apple.com/documentation/swift/lazysequence

 

 

[3] LazySequence 예제 (1)

문서에 나오는 lazy sequence 예제입니다. 

let doubled = [1, 2, 3].lazy.map { $0 * 2 }

 

map과 filter 같은 sequence operation은 eager 방식입니다. 즉 closure를 즉각적으로 사용해서 새로운 array를 리턴합니다.

lazy 프로퍼티를 사용하면 standard library에 closure과 sequence를 저장할 수 있는 명시적 권한을 부여하고 필요할 때 까지 계산을 연기할 수 있습니다. 

lazy sequence는 불필요한 스토리지 할당 및 계산을 피하는 데 사용될 수 있습니다.

 

 

[4] LazySequence 예제 (2)

문서의 예제보다  이 글의 예제가 필요성을  더 잘보여주는 것 같습니다! 

1에서 1000까지 숫자 중에 짝수인 숫자들을 걸러 2를 곱하고, prefix  2개를 구한다고 해봅시다. 

lazy sequence 를 사용해서 적은 연산량으로 이 작업을 수행할 수 있습니다. 

(1...1000).lazy
    .filter { $0 % 2 == 0 }
    .map { $0 * 2 }
    .prefix(2)

 

 

만약 lazy를 안사용했으면 filter에서 1000번 연산, map에서 500번 연산.. 이렇게 연산횟수가 많아지는데 

 

sequence

 

lazy 를 사용하면 연산횟수가 8번으로 줍니다. 

 

lazy sequence

 

위의 [용어 정리] 에서 살펴본 것처럼 

lazy는 '요소 하나하나에 지정한 연산을 한꺼번에 적용' 하기 때문에 아래와 같이 8번 연산을 하게 되는 것입니다. 

 

[ element가 1일 때 ] // 연산횟수: 1번 

filter

 

[ element가 2일 때 ] //  연산횟수:  3번 

filter

map

prefix

 

[ element가 3일 때 ] //  연산횟수:  1번 

filter

 

[ element가 4일 때 ]  //  연산횟수:  3번

filter

map

prefix

 

 

 

[5] LazySequence 타입

lazy 를 사용하면 타입이 조금 복잡해집니다;;

filter,  map 연산을 했을 경우,,  풀어서 써보면 아래와 같습니다. 

let array: LazySequence<Array<Int>> = [1,2,3].lazy
let filteredArray: LazyFilterSequence<Array<Int>> = array.filter { $0 % 2 == 1 }
let mappedArray: LazyMapSequence<LazyFilterSequence<Array<Int>>, Int> = filteredArray.map { $0 * 2 }

print(mappedArray)
// LazyMapSequence<LazyFilterSequence<Array<Int>>, Int>(_base: Swift.LazyFilterSequence<Swift.Array<Swift.Int>>(_base: [1, 2, 3], _predicate: (Function)), _transform: (Function))

print(Array(mappedArray))
// [2,6]

 

연산자를 여러개 체이닝 하면 타입이 매우 복잡해질 것 같으니.. 

array 전체가 필요하다면 Array로 타입변환해서 써야할 것 같네요! 

 

만약 특정 index의 element가 필요한 경우라면,  Array 변환 필요 없습니다!

let results = [1,2,3].lazy
    .filter { $0 % 2 == 1 }
    .map { $0 * 2 }

print(results.first)  // Optional(2)
print(results[0]) // 2

print(results) // LazyMapSequence~~~~
print(Array(results)) // [2,6]

 

 

 

반응형

'🍏 > Swift' 카테고리의 다른 글

[Swift Collections] deque  (1) 2022.10.08
[Swift] some, any 키워드  (2) 2022.07.16
[Swift] @inlinable과 @usableFromInline  (1) 2022.05.17
[Swift] Operator Overloading / Custom Operator  (0) 2022.02.02
[Swift] Result Builder 개념과 예제  (0) 2022.02.01
댓글