[Swift] 고차함수 맵, 필터, 리듀스에 대해 알아보자!

zooneon·2020년 11월 13일
0

Swift 기본 문법

목록 보기
8/14

본 내용은 '스위프트 프로그래밍' 책을 학습한 후 이를 바탕으로 작성한 글입니다.

맵, 필터, 리듀스

매개변수로 함수를 갖는 함수를 고차함수라고 부르는데, 스위프트에 대표적인 고차함수로는 맵, 필터, 리듀스 등이 있다.

Map

  • map 은 자신을 호출할 때 매개변수로 전달된 함수를 실행하여 그 결과를 다시 반환해주는 함수이다.

  • map 을 사용하면 컨테이너가 담고 있던 각각의 값을 매개변수를 통해 받은 함수에 적용한 후 다시 컨테이너에 포장하여 반환한다.

    → 기존 컨테이너의 값은 변경되지 않고 새로운 컨테이너가 생성되어 반환됨

let numbers: [Int] = [0, 1, 2, 3, 4]

var doubleNumbers = numbers.map({ (number: Int) -> Int in
    return number * 2
})
let strings = numbers.map({ (number: Int) -> String in
    return "\(number)"
})

print(doubleNumbers)    //[0, 2, 4, 6, 8]
print(strings)    //["0", "1", "2", "3", "4"]

//간결하게 표현도 가능하다
//매개변수 및 반환 타입 생략
doubleNumbers = numbers.map({ return $0 * 2 })
print(doubleNumbers)    //[0, 2, 4, 6, 8]

//반환 키워드 생략
doubleNumbers = numbers.map({ $0 * 2})
print(doubleNumbers)    //[0, 2, 4, 6, 8]

//후행 클로저 사용
doubleNumbers = numbers.map{ $0 * 2 }
print(doubleNumbers)    //[0, 2, 4, 6, 8]

필터 Filter

  • 필터filter 는 컨테이너 내부의 값을 걸러서 추출하는 역할을 하는 고차함수이다.
  • map 과 마찬가지로 새로운 컨테이너에 값을 담아 반환한다.
  • filter 함수의 매개변수로 전달되는 함수의 반환 타입은 Bool 이다.
let numbers: [Int] = [0, 1, 2, 3, 4 , 5]

let evenNumbers: [Int] = numbers.filter { (number: Int) -> Bool in
    return number % 2 == 0
}

let oddNumbers: [Int] = numbers.filter { $0 % 2 == 1 }

print(evenNumbers)    //[0, 2, 4]
print(oddNumbers)    //[1, 3, 5]

리듀스 Reduce

  • 리듀스 reduce 는 컨테이너 내부의 컨텐츠를 하나로 합하는 기능을 실행하는 고차함수이다.
  • 스위프트의 리듀스는 두 가지 형태로 구현되어 있다.
public func reduce<Result>(_ initialResult: Result,
		_ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result

→ 첫 번째 리듀스는 클로저가 각 요소를 전달받아 연산한 후 값을 다음 클로저 실행을 위해 반환하며 컨테이너를 순환하는 형태이다.
initialResult라는 매개변수를 통해 초깃값을 지정해주고 nextPartialResult라는 매개변수를 통해 클로저를 전달받는다.
nextPartialResult 클로저의 첫 번째 매개변수는 리듀스 메서드의 initialResult 매개변수를 통해 전달받은 초깃값 또는 이전 클로저의 결괏값이다.
nextPartialResult 클로저의 두 번째 매개변수는 리듀스 메서드가 순환하는 컨테이너의 요소이다.

public func reduce<Result>(into initialResult: Result,
		_ updateAccumulatingResult: (inout Result, Element) throws -> ()) rethrows ->
		Result

→ 두 번째 리듀스는 컨테이너를 순환하며 클로저가 실행되지만 클로저가 따로 결괏값을 반환하지 않고 inout 매개변수를 사용하여 초깃값에 직접 연산을 실행하게 된다.
updateAccumulatingResult 클로저의 첫 번째 매개변수는 리듀스 메서드의 initialResult 매개변수를 이용해 전달받은 초깃값 또는 이전에 실행된 클로저 때문에 변경되어 있는 결괏값이다.
updateAccumulatingResult 클로저의 두 번째 매개변수는 리듀스 메서드가 순환하는 컨테이너 요소이다.

let numbers: [Int] = [1, 2, 3]

//첫 번째 형태의 reduce
var sum: Int = numbers.reduce(0, { (result: Int, next: Int) -> Int in
    print("\(result) + \(next)")
    //0 + 1
    //1 + 2
    //3 + 3
    return result + next
})

print(sum)    //6

var subtractFromThree: Int = numbers.reduce(3) {
    print("\($0) - \($1)")
    //3 - 1
    //2 - 2
    //0 - 3
    return $0 - $1
}

print(subtractFromThree)    //-3

//두 번째 형태의 reduce
sum = numbers.reduce(into: 0, { (result: inout Int, next: Int) in
    result += next
})

print(sum)    //6

subtractFromThree = numbers.reduce(into: 3, {
    $0 -= $1
})

print(subtractFromThree)    //-3
profile
블로그 이전했습니다. https://blog.zooneon.dev

0개의 댓글