[Swift] 고차함수

parkgyurim·2022년 5월 14일
1

Swift

목록 보기
3/3
post-thumbnail

고차함수 (Higher-Order Function)

📌 인자 (Argument) 로 함수를 전달 받거나 실행 결과로 함수를 반환하는 함수

Swift 의 함수 (클로저) 는 일급 객체이므로 함수의 인자로 함수를 전달할 수 있고 실행 결과로 함수를 반환할 수 있습니다.
→ 즉, 고차함수 가 될 수 있습니다!!

일급 객체 (일급 시민)

"다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체를 가리킨다.
보통 함수에 인자로 넘기기, 수정하기, 변수에 대입하기 와 같은 연산을 지원할 때 일급 객체라고 한다."

위키 백과 - 일급 객체

고차 함수컨테이너 타입 (Array, Set, Dictionary 등) 에 사용될 수 있고, 이들이 가진 각 요소에 대해 동작합니다.

Swift 표준 라이브러리에서는 map, compactMap, flatMap, reduce, filter, contains, sorted, forEach, removeAll 등을 제공하고 있으며, 일반적으로 후행 클로저 (Trailing Closure) 를 사용해서 표현합니다.

이 중에서 대표적인 map, filter, reduce 를 한번 다루어 보겠습니다!


Map

변형

Returns an array containing the results of mapping the given closure over the sequence’s elements.

🔗 https://developer.apple.com/documentation/swift/sequence/3018373-map

map전달된 클로저로 변형한 객체를 리턴합니다.

var floatNumbers : [Float] = [1.0, 2.0, 3.5, 4.0]
var integerNumbers : [Int] = floatNumbers.map { (num : Float) -> Int in 
  								return Int(num) 
  							 }
  
print(integerNumbers) // [1, 2, 3, 4]

map 의 인자로 클로저가 전달되었고 클로저내에서 배열 각 요소에 대해 형변환이된 정수 배열을 반환합니다.

물론 클로저에서 자주 쓰는 타입 추론, 리턴 구문 생략, 파라미터 이름 생략 으로 더 간결하게 표현할 수 있습니다.

var integerNumbers : [Int] = floatNumbers.map { Int($0)! }

이걸 일반적으로 사용하는 for-loop 로 표현하면 조금 더 길고 복잡한 코드로 바뀌겠죠?

var floatNumbers : [Float] = [1.0, 2.0, 3.5, 4.0]
var integerNumbers : [Int] = []

for num in floatNumbers {
	integerNumbers.append(Int(num))
}

Filter

조건을 만족하는 요소 추출

Returns an array containing, in order, the elements of the sequence that satisfy the given predicate.

🔗 https://developer.apple.com/documentation/swift/sequence/3018365-filter

filter전달된 클로저의 조건을 만족하는 객체를 리턴합니다.
filter로 전달되는 클로저의 리턴 타입은 Bool 이며 true일 경우 해당 요소를 리턴합니다.

var numbers : [Int] = Array(1...10)
var evenNumbers : [Int] = numbers.filter { $0 % 2 == 0 }

print(evenNumbers) // [2, 4, 6, 8, 10]

Reduce

누적, 결합

Returns the result of combining the elements of the sequence using the given closure.

🔗 https://developer.apple.com/documentation/swift/sequence/2907677-reduce

reduce초기값클로저, 두 개의 파라미터를 전달받고
각 요소에 대하여 클로저 내에서 처리하여 초기값에 누적하여 결합할 수 있습니다.

var numbers : [Int] = [1, 2, 3, 4, 5]
var factorialNum : Int = numbers.reduce(1) { (result : Int, num : Int) -> Int
												return result *= num
                                           }
print(factorialNum) // 120

이처럼 배열을 순회하며 각 요소에 대해 누적하여 처리할 수 있습니다.

var factorialNum : Int = numbers.reduce(1) { $0 *= $1 }

Swift 에서는 연산자 역시 두 개의 파라미터를 받는 함수이므로 아래와 같이 표현할 수 있습니다.

var factorialNum : Int = numbers.reduce(1, *)

마무리

그래서 고차함수를 사용하면 뭐가 좋을까요?

  • 간결한 코드
    가끔 프로그래머스에서 나는 20줄인데 한줄짜리 다른 사람 풀이 보고 놀란적 있다 없다..?
    코드의 길이가 짧아지는 것 뿐만 아니라 깔끔해지고 가독성이 좋아지는 것 같습니다.
  • 재사용
    고차 함수의 전달되는 클로저를 재사용할 필요가 있을 경우, 변수에 클로저를 저장하고 재사용!
  • 컴파일러 성능
    컴파일 최적화 성능이 더 좋다.
    하지만 시간 복잡도 (Time Complexity) 측면에서는 성능 향상이 없습니다. → O(n)

오늘은 깔끔하고 간결한 코딩을 할 수 있게 해주는 Swift 의 고차함수에 대해 알아봤습니다.
위의 대표적인 3가지 이외에도 Swift 에는 유용한 고차함수들이 많은데 한번 둘러보시길 추천합니다!

Apple Swift Documentation - Sequence
https://developer.apple.com/documentation/swift/sequence

틀린 정보 또는 궁금한 점이 있다면 댓글 부탁드립니다! 읽어주셔서 감사합니다‼️

0개의 댓글