티스토리 뷰

Swift

Swift ) Sequences와 Lazy

Zedd0202 2018. 1. 16. 17:45
반응형

안녕하세요 :) Zedd입니다.

< Inheritance (상속) >글을 쓰다가...Lazy랑 프로퍼티 옵저버의 관계를 알아보다가.......한가지를 발견했습니다. 

바로 Sequences와 Lazy의 관계 XD

Sequences라 함은 뭐 배열이나 사전이 될 수 있겠네요.

이 둘 사이의 관계가 뭔지 < Being Lazy >글을 기반으로 알아보도록 하겠습니다.



Sequences와 Lazy



lazy에 관해서는 < 저장프로퍼티 >글에서 정리를 했으니, 참고하세요 ~.~

자..코드를 하나 볼거에요!



  1. func increment(x: Int) -> Int {

        print("Computing next value of \(x)")

        return x+1

    }


    let array = Array(0..<10)

    let incArray = array.map(increment)

    print("Result:")

    print(incArray[0], incArray[4])


자..차근차근 봅시다.

먼저 increment라는 함수는 조금 이따가 보기로 하고



  1. let array = Array(0..<10)

    let incArray = array.map(increment)

    print("Result:")

    print(incArray[0], incArray[4])



이 부분을 봅시다. 먼저 array를 하나 만들었어요! 0부터 10전까지니까 0부터 9까지가 array에 담기겠네요.

그리고 map이라는 고차함수를 array로부터 호출하네요. 그리고 map안에 아까 만든 incrment라는 함수를 넣었습니다.

map개념은 <map 개념>글에서 정리를 했었는데...


궁금증이 들 수 있죠.

저렇게 "함수"를 map안에 넣을 수 있나요?? 네!! 넣을 수 있습니다. map자체가 closure를 받기 때문이죠. 함수도 클로져였죠? 그것도 이름이 있는 ㅎㅎ

이제 그럼 incrment라는 함수를 봅시다.



  1. func increment(x: Int) -> Int {

        print("Computing next value of \(x)")

        return x+1

    }



map은 mapping closure를 받고, 타입을 변경하거나 값을 변경하여 Array를 돌려주죠? 해당 mapping closure는 array의 모든 요소에 적용됩니다.

그럼 이 increment라는 함수는 Int타입 요소를 하나 받아 해당 요소+1한 값, 즉 Int타입을 리턴해주는 함수네요.

이걸 굳이 이렇게 함수로 안만들고 map안에 mapping closure로 넣어주고싶다!!하면




  1. let incArray = array.map({(value : Int) -> Int in return value+1 })



이렇게 쓰면 되겠죠? 이렇게 쓴건 하나도 축약을 안한거지만 축약을 하게된다면




  1. let incArray = array.map({$0+1})



이렇게 쓸 수 있겠네요.

지금 lazy하고있는데..map설명을...

아무튼!!! map안에 저렇게 함수가 들어갈 수 있어요. 

자.. 그리고 



  1. let array = Array(0..<10)

    let incArray = array.map(increment)

    print("Result:")

    print(incArray[0], incArray[4]) //1 5




print문에 1과 5가 나오겠죠?

왜냐?? 처음 array는 [0, 1, 2.....9]

였는데 우리가 increment함수를 거쳐서 incArray를 만들었죠? 그건 array의 모든 요소를 +1한 값들의 배열이니까

[1, 2, 3....10]이라는 배열이 incArray에 들어가 있을거에요. 

그런데...

이제 본론으로 들어가보면,

위 코드를 사용하면 incArray 값에 액세스하기 전에 모든 출력 값이 계산됩니다..



그러니까 

1 ) 모든 요소를 1증가시켜주고 2) Result출력 3) 0,4번째 인덱스 출력 

이 과정인거죠.

근데 increment가 지금은 10이지만, 10000이라면? 또 엄청 크다면? increment함수 내에서 시간이 오래 걸린다면? 

굉장한 손해인거죠. 그렇게!!힘들게 map연산을 했지만, 우리가 필요한건 0, 4번째 인덱스 밖에 없는데..

이걸 해결 할 수 있는 것이 lazy입니다. 

Swift 표준 라이브러리에서 SequenceType 및 CollectionType 프로토콜에는 lazy라는 연산 프로퍼티가 있으며, 이 클래스는 특수 LazySequence 또는 LazyCollection을 반환합니다. 이러한 타입은 map, flatMap, filter 등과 같은 고차 함수를 게으른 방식으로 적용하는 데에만 사용됩니다.




  1. let array = Array(0..<1000)

    let incArray = array.lazy.map(increment)

    print("Result:")

    print(incArray[0], incArray[4])



이렇게 map연산 앞에 lazy라는 키워드를 붙히게 되면?





저~~기 위에서 lazy가 없었을 때의 결과와 조금 다르죠?



  1. let incArray = array.lazy.map(increment)


이 코드를 만나도 계산하지 않고.

Result가 출력된 후에, 이제 incArray에 접근할때!!!!!!!!!!

그때 계산을 시작합니다. 이게 lazy의 특징이었죠? 

또, 신기한점이 있는데!!!!

바로 0부터 9까지 array에 있는 모든 요소에 접근하는것이 아니라, 0번째 인덱스와 4번째 인덱스에만 접근하고 있습니다. 이렇게하면 퍼포먼스를 향상시킬 수 있겠죠?!?!?

이것이 Sequences에서 lazy를 사용하는 방법이랍니다. 



Chaining lazy sequences


Chaining으로 lazy를 사용할 수도 있는데요.

  1. func increment(x: Int) -> Int {

        print("Computing next value of \(x)")

        return x+1

    }

    func double(x: Int) -> Int {

        print("Computing double value of \(x)…")

        return 2*x

    }

    let array = Array(0..<10)

    let doubleArray = array.lazy.map(increment).map(double)

    print(doubleArray[3])



이렇게 연결해서 쓸 수도 있답니다.

차근차근 볼까요?

먼저 lazy니까..


  1. print(doubleArray[3])



이 코드를 만났을 때 연산을 시작하겠네요. 그것도 3번째 인덱스에 대해서만요.



  1. let doubleArray = array.lazy.map(increment).map(double)



3번째 인덱스는 3이겠죠? (0베이스니까)



  1. let doubleArray = array.lazy.map(increment).map(double)



먼저 increment함수를 거쳐 3이 4가 될거고, 그 뒤에 map도 이제 적용이 될거에요. double함수는 해당 요소를 2배로 만들어주는 거니까 4*2해서 결과는 8이 되게됩니다. 

아무튼 이런식으로 Sequences와 Lazy를 쓰시면 됩니당 :) 

신기하죠!?!?!?




반응형