[iOS] Swift Algorithm
Updated:
Swift Algorithms
Swift Algorithm은 오픈소스 패키지이다.Swift Algorithm에 내장된 알고리즘 기능을 사용하면 코드를 더 간결하게 작성할 수 있다.
Swift Algorithm이 제공하는 알고리즘 메서드를 살펴보기 전에 Swift Standard Library가 제공하는 기본 알고리즘 메서드를 살펴보도록 하자.
map
아래처럼 채팅방을 구성한다고 해보자. 흔히 많이 사용하는 map 이 for 보다 일반적인 상황에서 더 간결한 코드를 보여줄 수 있고, 성능면에서도 우수하다. 이유는 map 은 배열 사이즈 재설정을 위한 중간 할당을 하지않고 공간을 재사용하기 때문이다.
compactMap
위의 채팅방(사진)에서 사진들만 모으는 코드는 다음과 같이 역시 간결하게 나타낼 수 있다.
message가 사진이면 이를 모은다. 일반적인 for 문보다 filter와 map으로 간결하게 나타낼수 있다.

하지만 위의 코드를 compactMap으로 처리 가능하다. compactMap은 nil인 요소를 제외한 요소들로 이루어진 배열을 반환해준다.
joined, flatMap
하나의 요소를 배열로 mapping 해주는 주는 코드를 살펴보자.

하지만 위의 여러 배열들을 하나로 묶어주고 싶다면 어떻게 해야 할까? joined() 를 사용하면 된다.

그리고 위의 구현을 flatMap 으로 간단히 처리 가능하다.
prefix, suffix
배열의 가장 마지막 요소들만을 보여주고 싶은 코드를 살펴보자. prefix 는 요소의 가장 앞부분만을 반환한다.

반면 suffix 는 배열의 가장 뒷부분을 보여준다. 결론적으로 위와 동일한 결과를 보여주며 같은 결과라도 다른 알고리즘을 적용하여 유동적으로 대응할 수 있다.
알고리즘 성능
알고리즘의 각 chain(메서드)에서 중간 배열을 할당한다면, 일반 반복문보다 느릴수 있다. 하지만 swift에서는 보다 효율적으로 이 문제를 해결하고 있다.
joined()는 배열을 메모리에 새로 할당하지 않고FlattenSequence를 반환한다. 여기서FalttenSequence는lazy adaptor로 불린다. 이는 배열처럼 사용되지만 한번 더 감싼wrapper인 셈이다. 그렇기에 생성하기 위한 비용이 들지 않아 효율적이다. 게다가 “lazy” 하기 때문에 사용이 될때 할당이 된다.- 그 결과, FlattenSequence와 같은 lazy adaptor는 알고리즘 체인이 성능면에서 우수할 수 있도록 해준다.
- 아래의 코드에서는 바로
PhotoItem으로 반환되지 않고ArraySlice로,reversedCollection으로 wrrapping 된다.

compactMap앞에lazy를 적용하면 lazy하게 만들어 줄수 있다.lazy는 매우 큰 컬렉션에서 적은 수의 요소들만 사용하고자 할때 유용하다.

- 반면 그럼에도 배열이 필요하거나 사용하고 싶다면 Array로 감싸면 된다.
Swift Algorithm Package
Swift Algorithm Package는 Swift Standard Library가 아직 제공하지 않는 알고리즘 기능을 제공한다.

Swift Algorithm 이 제공하는 몇몇 interation 관련 알고리즘 메서드들은 다음과 같다.
windows(ofCount: Int)
컬렉션 요소들을 하나씩 전진하여 특정 count만큼 반복적으로 잘라 각각 배열로 반환해준다. 위에서 설명했듯이 subsequence를 반환하기 때문에 메모리에 공간을 할당하지 않는다.
adjasonedPairs()
windows(ofCount: Int) 와 기능은 비슷하지만 subsequence가 아닌 tuple을 반환한다.
chunks(ofCount: Int)
windows(ofCount: Int)와 기능은 비슷하지만 요소들을 하나씩 전진시켜 자르지 않고, count만큼 컬렉션에서 요소들을 잘라 반환한다. 컬렉션의 모든 요소가 count만큼 알맞게 나눠지지 않으면 나머지요소들이 반환된다.
chunked(on:)
컬렉션의 요소들을 돌며 특정 옵션에 맞는지에 대한 bool 값과 그에 대한 요소들로 구성된 배열의 튜플을 반환한다. 다음은 chunked(on:)을 사용하여 여러 숫자의 요소로 구성된 컬렉션에서 연속되는 소수의 요소들을 배열로 묶어 반환하는 예시이다.

이와 다른 옵션으로 이전 값과 현재 값이 다른지에 대한 로직을 만들때 chunked(on:)을 사용할 수 있다.

위의 지식을 바탕으로, 컬렉션에서 현재 요소가 생성된 시간과 이전에 생성된 요소의 시간차가 1시간 이내인 요소들을 분리해 배열로 만들고 그 사이에 각 배열들의 마지막 요소를 삽입해 다시 합치는 로직을 작성해 보자.
우선 컬렉션의 요소들중 각 시간차가 한시간 이내인 것들을 묶어 각 배열로 만들기 위해 chunked를 사용한다.
여기서 사용할 chunked는 predicate, 즉 조건을 사용하여 요소들을 분리해 배열로 만들 수 있다.

그 이후 joined 를 사용하여 시간단위로 요소들이 분리된 배열들 사이에 각 배열의 마지막 요소를 삽입해 합쳐준다.

그리고 맨 앞에 lazy를 적용해줄 수가 있다.

주의할 점이 있는데, lazy가 언제나 해결책이 될수는 없다는 점이다. 시퀀스를 한번만 사용해야 한다면 lazy는 효율적으로 사용될 수 있지만 그렇지 않고, 시퀀스를 반복적으로 사용해야 하면 다음과 같은 코드는 mapping, chunking, joining을 매번 반복하게 되고 이는 비용을 크게 소모한다.

이럴 경우는 Array로 한번 감싸주면 효율적으로 작업을 이어나갈 수 있다.

이처럼 Swift Standard Library와 Algorithm Package에서 제공하는 알고리즘 기능은 Sequence와 Collection Protocol을 따르는 Array나 String 같은 타입들에서 사용가능하다.
Leave a comment