[swift] Map, Filter, Reduce

Swift 2015. 6. 22. 23:09



함수를 인자로 취하는 함수를 고차원 함수(higher-order function)라고 합니다.
Swift 표준라이브러리에서 array의 메서드로 제공하는 고차원 함수인 map, filter, reduce에 대해서 알아보겠습니다.

1. Map

    func map<U>(transform: (T) -> U) -> Array<U>

Map은 배열 각 요소 x에 변환함수 transform을 적용하고 그 결과값으로 구성된 배열을 반환하는 함수입니다.
배열요소들을 다른 값으로 맵핑하는 함수이지요.

2. Filter

func filter(includeElement: (T) -> Bool) -> Array<T>

Filter는 조건식을 인자로 받아, 조건식이 true를 만족하는 요소들로만 구성된 배열을 반환하는 함수입니다.
쉽게 말하면, 배열요소들을 필터링하는 함수입니다.

3. Reduce

func reduce<U>(initial: U, combine: @noescape (U, T) -> U) -> U

Reduce는 U를 초기값으로 하여,각 배열요소들과 순차적으로 결합연산을 하여 누적된 단일값을 반환하는 함수입니다.


4. 사용예

이제 사용법에 대해서 알아볼까요? 다음과 같은 정수형 배열이 있습니다.

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  1. 1. 정수형 배열의 각 요소에 1을 더한 배열을 얻고 싶다.
  2. 2. 정수형 배열의 각 요소에 2를 곱한 배열을 얻고 싶다.
  3. 3. 정수형 배열에서 짝수로만 구성된 배열을 얻고 싶다.
  4. 4. 정수형 배열 값들의 총합을 얻고 싶다.

map 함수를 사용하여 1,2번 문제의 답을 쉽게 얻을 수 있습니다.

// 1. 정수형 배열의 각 요소에 2를 곱한 배열을 얻고 싶다.  
arr.map { (x) -> Int in
    return x+1
}

// 2. 정수형 배열의 각 요소에 2를 곱한 배열을 얻고 싶다.
arr.map { (x) -> Int in
    return x*2
}

filter를 사용하여 3번을 해결해보겠습니다.

// 3. 정수형 배열에서 짝수로만 구성된 배열을 얻고 싶다. 
arr.filter { (x) -> Bool in
    return x%2 == 0
}  

당연히 4번은 reduce의 몫입니다.

// 4. 정수형 배열 값들의 총합을 얻고 싶다.
arr.reduce(0, combine: { (result, x) -> Int in
   return result + x
})

Trailing Closure 를 사용하여 다소 낯선 형태지만 코드가 너무 짧아 놀라셨죠?   

(Trailing Closure는 함수의 마지막 인자가 클로져(함수)일 경우, 클로져를 함수 호출부의 꼬리(trailing)에 표기하는 방식입니다. )

이러한 고차원 함수들을 복합적으로 사용하여 프로그래밍하는 방식을 함수형 프로그래밍이라고 합니다. 

함수형 프로그래밍은 짧고 명료한 코드를 작성할 수 있게하고, 버그의 가능성 또한 훨씬 줄인다고 합니다.

낯선 함수형 프로그래밍 방식에 익숙해지려면 다소 시간이 걸리겠지요? ^^


5. Map 함수 구현하기

학습한 내용을 좀더 잘 이해하기 위해 직접 map 함수를 만들어 보겠습니다.

일반적인 방법으로 1,2번 문제를 구현하는 코드를 작성했습니다.

var arr = [1, 2, 3, 4, 5]

func pluseOne(arr:[Int]) -> [Int] {
    var result:[Int] = []
    for x in arr {
        result.append(x + 1)
    }
    return result
}

func multiplyTwo(arr:[Int]) -> [Int] {
    var result:[Int] = []
    for x in arr {
        result.append(x * 2)
    }
    return result
}  

두 함수는 연산하는 행위만 제외하고 거의 비슷합니다. 연산하는 행위를 함수로 묶어내면 일반화가 가능해보이네요.

func map(arr:[Int], transform:(Int->Int)) -> [Int] {
    var result:[Int] = []
    for x in arr {
        result.append(transform(x))
    }
    return result
}  

이제 제네릭을 사용하여 범용타입으로 대치하면

func map<T, U>(arr:[T], transform:(T->U)) -> [U] {
    var result:[U] = []
    for x in arr {
        result.append(transform(x))
    }
    return result
}

map 함수가 완성되었습니다. 우리는 글로벌 함수로 구현했지만 표준라이브러리에서는 array의 메소드로 제공한다는 점이 유일한 차이입니다.


5. Filter 함수 구현하기

Map 함수를 구현해보아서 Filter 함수 구현은 좀더 간단합니다.

// 일반적인 방법으로 구현하고
func filter(arr:[Int]) -> [Int] {
    var result:[Int] = []
    for x in arr {
        if x%2 == 0 {
            result.append(x)
        }
    }
    return result
}

// 함수로 공통 행위를 묶어낸 다음
func filter(arr:[Int], includeElement:(Int->Bool)) -> [Int] {
    var result:[Int] = []
    for x in arr {
        if includeElement(x) {
            result.append(x)
        }
    }
    return result
}


// 제네릭 적용
func filter<T>(arr:[T], includeElement:(T->Bool)) -> [T] {
    var result:[T] = []
    for x in arr {
        if includeElement(x) {
            result.append(x)
        }
    }
    return result
}


6. Reduce 함수 구현하기

단계적으로 구현해보니 고차원 함수 구현이 어렵지 않죠? 이제 reduce를 구현해 볼까요?

// 일반적으로 구현하고  
func reduce(arr:[Int]) -> Int {
    var result:Int = 0
    for x in arr {
        result = result + x
    }
    return result
}

// 초기값 할당과 누적하는 행위를 파라미터로 분리하고 
func reduce(arr:[Int], initial:Int, combine:(Int,Int)->Int) -> Int {
    var result:Int = initial
    for x in arr {
        result = combine(result, x)
    }
    return result
}

// 제네릭 적용 
func reduce<T, U>(arr:[T], initial:U, combine:(U, T)->U) -> U {
    var result:U = initial
    for x in arr {
        result = combine(result, x)
    }
    return result
}  

지금까지 Swift 표준라이브러리의 Map, Filter, Reduce에 대해 알아보았습니다.




'Swift' 카테고리의 다른 글

[swift2] Xcode7 beta2 Swift 언어 변경사항  (0) 2015.06.24
[swift] 문자열 Indexing과 Slicing  (0) 2015.06.23
[swift] Map, Filter, Reduce  (0) 2015.06.22
[swift2] New in Swift2.0  (0) 2015.06.19
[swift] Apple Swift Blog  (0) 2015.06.10
[swift] Swift 기초 - 4  (0) 2014.10.29