[Swift 프로그래밍] 고차함수(map, filter, reduce)

이정훈·2022년 6월 16일
0

Swift 기본

목록 보기
14/22
post-thumbnail

본 내용은 스위프트 프로그래밍 3판 (야곰 지음) 교재를 공부한 내용을 바탕으로 작성 하였습니다.

스위프트에서 함수는 일급 객체로서 매개변수의 전달인자로 함수를 전달 받는 함수를 고차 함수(high-order-function)이라고 한다.
고차 함수의 대표적인 예로 맵(map), 필터(filter), 리듀스(reduce)가 있다.

map(맵)


맵(map)은 매개변수로 함수를 전달인자로 받고 함수를 거쳐 결과 값을 반환하는 함수이다.
Sequence와 Collection protocol을 따르는 타입이 map을 사용 할 수 있으며, 기존에 가지고 있는 데이터가 함수를 거쳐 새로운 컨테이너를 반환하므로 map은 기존의 데이터를 변형 할때 사용된다.

다음은 1부터 5까지의 숫자를 테이터를 가지는 배열을 제곱의 값과 숫자를 문자로 바꾸는 map을 구현한 코드이다.

//map(맵)
let numbers: [Int] = [1, 2, 3, 4, 5]

var squaredNum = numbers.map({ (number: Int) -> Int in
    return number * number
})


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

print(squaredNum)   //[1, 4, 9, 16, 25, 36]
print(stringNum)    //["1", "2", "3", "4", "5", "6"]

//for문을 이용한 표현
var squaredNum: [Int] = [Int]()     //제곱한 수를 저장할 배열
var stringNum: [String] = [String]()    //숫자를 문자로 만들어 저장할 배열

for number in numbers {
    squaredNum.append(number * number)
    stringNum.append("\(number)")
}

print(squaredNum)   //[1, 4, 9, 16, 25, 36]
print(stringNum)    //["1", "2", "3", "4", "5", "6"]

map의 전달인자로 클로저가 전달된 모습을 확인 할 수 있다.
따라서 클로저의 특성인 후행 클로저와 반환 키워드 생략으로 다음과 같이 더욱 간단하게 구현이 가능하다.

squaredNum = numbers.map { $0 * $0 }
strings = numbers.map { ("\($0)") }

map 구문은 for in 구문과 비교해서 코드를 더 간결하게 표현 할 수 있다는 장점과 더불어 연산 시간에도 차이를 보인다.
for in 구문은 for문이 요소를 하나하나 돌면서 배열에 요소를 추가하면서 map 구문과 연산 차이가 발생하고 또한 요소를 추가 할 새로운 배열 선언에서도 연산의 차이가 발생한다.

filter(필터)


필터(filter)는 컨테이너 내부에 존재하는 값을 특정 기준에 부합하는 값만을 추출한다.

따라서 filter의 전달인자로 전달되는 클로저의 반환 타입은 Bool 타입이어야 한다.

다음은 filter 함수를 이용하여 홀수 값만을 추출하는 코드이다.

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

var evenNum: [Int] = numbers.filter( { (number: Int) -> Bool in
    return number % 2 == 0      //numbers에서 짝수만을 추출하여 새로운 배열에 저장
})

print(evenNum)      //[2, 4, 6]

이때 filter 함수의 numbers의 각 요소를 매개변수로 전달 후 반환 값이 Bool 타입으로 되어 있는데, 뒤의 실행문 수행 후 결과가 true이면 새로운 컨테이너에 값을 추가하게 된다.

이제 map 함수와 filter 함수를 연결하여 사용 할 수 있다.
다음은 map 함수를 이용하여 기존의 배열 요소에 5씩 더한 후 filter 함수를 이용하여 홀수의 값만을 추출하는 코드이다.

//map 함수와 filter 함수의 연계
let numbers: [Int] = [1, 2, 3, 4, 5, 6]

var oddNum: [Int] = numbers.map { $0 + 5 }.filter { $0 % 2 == 1}    //numbers의 각 요소에 5씩 더한 후 홀수 추출
print(oddNum)       //[7, 9, 11]

reduce(리듀스)

reduce(리듀스)는 컨테이너의 내부 요소를 하나씩 가져와 클러저의 연산 결과로 합해주는 고차함수이다.

reduce는 두가지 형태가 존재하는데, 첫번째 형태는 클로저가 컨테이너 내부 요소를 하나씩 전달 받아 클로저의 연산이 수행 될때 마다 결과 값을 반환하면서 순회하는 형태이며, 스위프트에서 정의는 다음과 같다.

//reduce 정의
public func reduce<Result>(_ initialResult: Result, 
	_ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result

initialResult 매개변수에 초기값을 전달 받으며, nextPartialResult 매개변수로 클로저를 전달 받는다.

또한 클로저의 첫 번째 매개변수 Result의 자리에는 첫 번쨰 순회에서는 초기값이 두번째 순회 이후부터는 이전에 수행된 결과의 결과 값이 전달되며, 마지막 순회까지 순회 후 연산의 결과 값이 반환 되는 것을 볼 수 있다.
그리고 클로저의 두번째 매개변수 Element의 자리에는 컨테이너의 내부 요소들이 전달된다.

다음은 첫번째 형태의 reduce 함수를 구현한 예시이다.

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

var sum: Int = numbers.reduce(0, { (result: Int, next: Int) -> Int in
    return result + next    //초기값이 0이고 numbers에 모든 요소를 더하는 리듀스
})

print(sum)      //21

var mul: Int = numbers.reduce(1) {  //초기값이 1이고 numbers의 모든 요소를 곱하는 리듀스
    $0 * $1     //후행 클로저, return 생략, 매개변수 생략
}

print(mul)  //720

numbers의 모든 요소를 더한는 reduce와 모든 요소를 곱하는 reduce를 구현하였다. 물론 map, filter와 마찬가지로 클로저의 특성을 활용하여 간략하게 표기 가능하다.

다음 두번째 reduce의 형태는 클로저의 결과 값을 반환하지 않고 inout 매개변수로 초기값을 직접 참조하여 연산을 수행하는 형태이며, 스위프트에서 정의는 다음과 같다.

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

첫번째 매개변수 into에는 초기 값이 전달되며, 클로저를 전달받는 매개변수 updateAccumulatingResult의 첫번째 인자로 inout(참조)를 전달 받는 것을 확인 할 수 있으며, 초기값을 참조하여 연산을 수행하고 결과 값을 저장하기 때문에 결과적으로 컨테이너 내부 요소 순회가 끝나면 초기값이 결과값이 될 것이다. (정의 부분에도 클로저의 반환값이 없는 것을 확인할 수 있다.)

다음은 두번째 형태의 reduce 함수를 구현한 예시이다.

let numbers: [Int] = [1, 2, 3, 4, 5, 6]
  
sum = numbers.reduce(into: 0, { (result: inout Int, next: Int) in
    result += next  //값을 반환하지 않고 inout으로 직접 연산 수행
})

print(sum)      //21

mul = numbers.reduce(into: 1) {
    $0 *= $1    //후행 클로저, return 생략, 매개변수 생략
}

print(mul)      //720
profile
새롭게 알게된 것을 기록하는 공간

0개의 댓글