본문으로 바로가기

[Swift] 고차함수 map, filter, reduce

category Swift/문법 2021. 10. 3. 23:54

안녕하세요 :) 

jimin이에요

 

백준이나 프로그래머스에서 알고리즘 공부를 하며 다른사람의 풀이를 보면 엄청 간결하게 짜인 코드가 몇몇 보이더라구요

그런 코드들을 보면 map, filter, reduce같은 고차함수가 사용된게 많더라구요

그래서 오늘은 스위프트의 고차함수에 대해서 알아볼려구 해요

 

고차함수란?

스위프트는 함수를 일급객체로 취급해요

따라서 함수를 다른 함수의 전달인자로 사용할 수 있답니다

매개변수로 함수를 갖는 함수를 고차함수라고 해요

스위프트의 대표적인 고차함수로는 map, filter, reduce 등이 있답니다

 

1. map

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

스위프트의 Sequence, Collection 프로토콜을 따르는 타입은 모두 맵을 사용할수 있어요

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

let doubleNumbers = numbers.map({ (number: Int) -> Int in
    return number * 2
})

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

print(numbers)              // [0, 1, 2, 3, 4, 5]  기존 배열은 변하지 않는다
print(doubleNumbers)        // [0, 2, 4, 6, 8, 10]
print(strings)              // ["0", "1", "2", "3", "4", "5"]

맵을 사용하면 컨테이너가 가지고 있는 각각의 값을 매개변수를 통해 받은 함수에 적용후 다시 새로운 컨테이너에 포장하여 반환해요

기존 컨테이너의 값은 변하지 않는답니다

그래서 맵은 기존 데이터를 변형하는데 많이 사용돼요

 

물론 map의 기능은 for문이랑 크게 다를게 없지만 코드의 재사용이나 컴파일러 최적화 측면에서 본다면 성능에 차이가 있어요

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

var doubleNumbers = [Int]()
var strings = [String]()

for i in numbers {
    doubleNumbers.append(i * 2)
    strings.append("\(i)")
}

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

for문으로 같은 기능을 하게 짠 코드에요

map메서드를 사용했을때가 for문을 사용한 것보다 편리하게 각 요소의 연산을 실행해요

또한 배열의 append연산을 실행하기 위한 시간도 필요가 없답니다

2. filter

filter란 컨테이너 내부의 값을 특정 조건에 맞게 걸러서 추출해줘요

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

let evenNumber: [Int] = numbers.filter { number in
    return number % 2 == 0 // 짝수만 리턴
}

print(evenNumber)   // [0, 2, 4]

3. reduce

reduce는 컨테이너 내부의 콘텐츠를 하나로 합하는 기능을 실행해요

스위프트의 reduce에는 두가지 형태로 구현돼있는데 하나하나 알아보도록 할게요

먼저 reduce(_:_:) 형태에요

reduce(initalResult: Result, nextParatialResult: (Result, Element) throws -> Result)

initalResult라는 매개변수로 전달되는 값을 통해 초기값을 지정해줄 수 있으며 nextParatialResult라는 이름의 매개변수로 클로저를 전달받아요

nextParatialResult의 첫번째 매개변수는 initalResult매개변수를 통해 전달받은 초기값 또는 이전 클로저의 결과값이에요

두번째 매개변수는 리듀스 메서드가 순환하는 컨테이너의 요소랍니다

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

var sum: Int = numbers.reduce(0, { (result: Int, next: Int) -> Int in
    print("\(result) + \(next)")
    /*
     0 + 0
     0 + 1
     1 + 2
     3 + 3
     6 + 4
     10 + 5

     */
    return result + next
})

print(sum)      // 15

예제를 한번 볼게요

initalResult 매개변수는 0으로 지정해줬어요

즉 nextParatialResult매개변수의 클로저에 있는 result에 처음으로 들어가는 값은 0이 되는거죠

next에는 numbers의 첫번째 요소가  인자로 들어가요

그 다음 result + next의 값이 다음 result로 들어가게되고 next에는 그 다음번 요소가 인자로 들어가죠

이 과정을 반복해가며 numbers의 마지막 요소까지 더하게 되면 그 값을 sum으로 리턴해준답니다

 

두번째로는 reduce(into:_:) 형태를 알아볼게요

reduce(into: Result, updateAccumulatingResult: (inout Result, Element) throws -> ())

똑같습니다

reduce(_:_:)형태랑 거의 똑같아요

다만 다른점이 있으면 클로저가 따로 결과값을 반환하지 않는 형태라는 점이에요

대신 inout 매개변수를 사용하여 초깃값에 직접 연산을 실행하게 돼요

var sum: Int = numbers.reduce(into: 0) { (result: inout Int, next: Int) in
    print("\(result) + \(next)")
    /*
    0 + 0
    0 + 1
    1 + 2
    3 + 3
    6 + 4
    10 + 5
     */
    result += next
}

print(sum)      // 15

 

 

오늘은 스위프트의 매개변수에 대해서 알아봤어요

물론 이번에 알아본건 아주아주 기초에 불과하지만 어떻게 사용하냐에 따라 그 활용도가 높아지는게 고차함수라고 생각해요

앞으로 알고리즘 공부를 할때 고차함수를 활용하도록 생각을 많이 해봐야겠어요

 

그럼 안녕히계세요! :>