[Swift] 고차함수와 클로저 축약 표현

Ryan (Geonhee) Son·2021년 4월 4일
0

Study Stack

목록 보기
2/34


고차함수는 다른 함수를 전달인자로 받거나 함수 실행의 결과를 함수로 반환하는 함수’를 의미합니다. map(_:), filter(_:), reduce(_:_:)와 같은 종류가 있으며, Swift에는 collection 타입인 Array, Set, Dictionary 등에 구현되어 있습니다.

map

map(_:)은 클로저를 매개변수로 입력 받아 컨테이너 내부의 기존 데이터를 변형하여 새로운 컨테이너를 생성합니다.

예시를 위해 Int 타입을 요소로 가지는 배열을 선언해보겠습니다.

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

아래는 map(_:) 메서드를 자동완성한 모습입니다.

메서드의 역할에 맞게 매개변수의 이름이 transform으로 설정되어 있네요. 모양을 보니 컨테이너 내부의 요소 하나하나를 변형(transform)하여 원하는 타입으로 반환해주는 클로저를 요구하고 있습니다. 후행 클로저 (Trailing closure)로 표현하면 아래와 같습니다.

그럼 numbers 배열의 요소를 두 배로 만들어 Double 타입으로 변환하여 반환해보겠습니다.

let doubledNumbers = numbers.map { (item: Int) -> Double in
    return Double(item * 2)
}

map(_:) 메서드의 transform 매개변수의 클로저는 Int 타입의 요소를 매개변수로 받아 어떠한 타입 (제네릭 타입 T)로 반환한다는 것을 이미 알고 있습니다. 그렇기 때문에 매개변수와 반환 타입을 아래와 같이 생략해줄 수 있습니다.

let doubledNumbers = numbers.map { (item) in
    return Double(item * 2)
}

그리고 이미 이 클로저에서 매 번 하나의 T 타입 반환 값이 있음을 알고 있으므로 return 키워드도 생략이 가능합니다. 클로저의 내용이 짧으므로 한 줄으로 작성할 수 있겠습니다.

let doubledNumbers = numbers.map { (item) in Double(item * 2) }

위의 코드에서 클로저 안의 in 키워드는 매개변수와 반환값을 구분해주는 역할을 합니다.
마지막으로 Swift는 map(_:) 메서드의 클로저에 하나의 매개변수를 가지는 것을 이미 알고 있으므로 매개변수의 이름을 $0으로 지칭할 수 있습니다. 이 축약형 이름은 클로저의 매개변수가 입력되는 순서대로 $0, $1, $2, ...와 같이 지칭할 수 있습니다. 미리 예약된 키워드인 $0을 사용하면 매개변수의 이름을 짓기 위한 공간인 in 이전의 내용도 제거할 수 있겠죠? 그렇게 하면 아래와 같은 코드가 완성됩니다.

let doubledNumbers = numbers.map { Double($0 * 2) }

멋지지 않나요? 계속해서 다른 고차함수들을 보러 가시죠!

filter

filter(_:) 메서드는 isIncluded 이름을 매개변수로 받는 고차함수로, 컨테이너 내부의 값을 걸러서 새로운 컨테이너로 추출하는 기능을 수행해줍니다. map과는 달리 문서화 주석에 별다른 내용이 없네요. 사용해보면서 알아보도록 하겠습니다.

자동 완성의 결과는 아래와 같습니다. 걸러주는 역할에 맞게 배열의 요소를 하나씩 꺼내서 조건에 맞는지 판단하여 Bool 타입을 반환해주네요.


filter(_:) 메서드를 이용하여 짝수인 수만 걸러내어 새로운 배열을 만들어보겠습니다. map(_:) 메서드와 동일하게 한 단계씩 축약해볼게요.

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

// 전달인자와 반환 타입 생략
let evenNumbers = numbers.filter { (item) in
    return item % 2 == 0
}

// return 키워드 생략
let evenNumbers = numbers.filter { (item) in item % 2 == 0 }

// 전달인자 축약 표현
let evenNumbers = numbers.filter { $0 % 2 == 0 }

reduce

reduce(_:_:) 메서드는 컨테이너 내부의 요소들을 하나로 통합하는 기능을 수행합니다. 메서드 이름이 직관적으로 와닿지 않는데, 컨테이너 내부의 요소에 클로저를 통해 전달 받은 작업을 하여 줄이는(reduce) 역할을 수행한다고 생각하시면 조금 더 친숙하게 느껴질 것 같습니다. 자동 완성한 모습부터 보시죠.

첫 번째 매개변수는 initialResult로 다음 매개변수인 nextPartialResult에 입력된 클로저를 수행하기 전 최초(결과)값입니다. 최초값으로부터 시작하여 nextPartialResult에 입력된 클로저의 동작을 컨테이너 내부의 요소를 하나씩 꺼내어 수행하는 형식으로 동작합니다. 동작 예시를 보시면 쉽게 이해하실 수 있으실 것입니다. 초깃값 0으로 시작하여 컨테이너 내부의 요소들을 모두 더해나가는 예시로 들겠습니다.

let sumOfNumbers = numbers.reduce(0) { (partialResult: Int, currentItem: Int) -> Int in
    return partialResult + currentItem
}

// 전달인자와 반환 타입 생략
let sumOfNumbers = numbers.reduce(0) { (partialResult, currentItem) in
    return partialResult + currentItem
}

// return 키워드 생략
let sumOfNumbers = numbers.reduce(0) { (partialResult, currentItem) in partialResult + currentItem }

// 전달인자 축약 표현
let sumOfNumbers = numbers.reduce(0) { $0 + $1 }

reduce(_:_:) 메서드, 정말 재미있지 않나요? 동작하는 방식도 한 번 보고 싶네요. 중간 결과들을 print 해보면 아래와 같은 모습을 관찰할 수 있습니다.

이런 과정으로 연산해서 reduce를 수행하는군요. 마지막으로 105를 더한 결과가 sumOfNumbers 상수에 할당되겠네요.

지금까지 Swift의 고차함수에 대해 알아보았습니다! 다음에 봐요~!

profile
합리적인 해법 찾기를 좋아합니다.

0개의 댓글