大器晩成

고차함수 본문

iOS/Swift 문법

고차함수

zerobugpark 2025. 3. 2. 16:52

 

고차함수란?

  • 함수를 파라미터로 사용하거나, 함수의 실행 결과를 함수로 리턴하는 함수를 말합니다.
  • 일반적으로 함수형 언어를 지향하는 언어들에 기본적으로 구현되어 있으며, 크게 3가지가 있습니다.
  • map / filter / reduece (참고: forEach, compactMap, flatMap)
  • Sequence, Collection 프로토콜을 따르는 컬렉션(배열, 딕셔너리, 세트 등)에 기본으로 구현되어 있습니다.
  • Optional 타입에도 구현되어 있습니다.

 

 

1. map

  • 기존의 배열 등의 각 아이템을 새롭게 매핑해서 새로운 배열을 리턴하는 함수입니다.
  • 각 아이템을 매핑/변형해서 새로운 배열을 만들 때 사용합니다.
  • 리턴 타입은 어떤 타입으로든 가능합니다.
// 예제 1
let numbers = [1, 2, 3, 4, 5]

var numbersString = numbers.map { num in
    return "numbers의 숫자: \(num)"
}

print(numbersString)
//["numbers의 숫자: 1", "numbers의 숫자: 2", "numbers의 숫자: 3", "numbers의 숫자: 4", "numbers의 숫자: 5"]


// 예제 2
let square = numbers.map {
    return $0 * $0
}

print(square) // [1, 4, 9, 16, 25]

// 예제 3
let alphabet = ["A", "B", "C", "D", "E", "F"]

let dobleAlphabed = alphabet.map {
    return $0 + $0
}

print(dobleAlphabed) // ["AA", "BB", "CC", "DD", "EE", "FF"]

 

2. filter

  • 기존 배열 등의 각 아이템의 조건을 확인 후 참을  만족하는 아이템을 걸러내서 새로운 배열을 리턴합니다.
  • 각 아이템을 필터링해서 새로운 배열을 만들 때 사용합니다.
// 예제 1
let nubmer = [1, 2, 3, 4, 5, 6]
let even = nubmer.filter {
    return $0 % 2 == 0
}

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

// 예제 2
func isOdd(_ i: Int) -> Bool {
    return i % 2 != 0
}

let odd = numbers.filter(isOdd) // 클로저가 아닌 함수로 전달해도 가능


print(odd)


// 예제 3
let names = ["Jobs", "Tim", "Young"]
var name = names.filter {
    return $0.contains("Y") // 해당 문자에 Y가 포함되어있으면 참 아니면 거짓
}

print(name)

 

 

다중 filter

// 예제 1
let evenNumber2 = numbers.filter {
    $0 % 2 == 0
}.filter {
    $0 < 5
}
print(evenNumber2) //[2,4]

// 예제 2
let evenSquare = numbers.filter {
    $0 % 2 == 0
}.map {
    $0 * $0
}
print(evenSquare) //[4, 16, 36]
  • 필터 함수를 연속으로 사용할 수도 있습니다.
  • 위의 경우에 짝수를 먼저 판단 후, 나온 배열을 다시 필터를 통해 5보다 작은 값만 리턴하는 형태로 구성되어 있습니다.
  • map 함수랑도 같이 사용할 수 있습니다.

3. reduce

  • 기본 배열 등의 각 아이템을 클로저가 제공하는 방식으로 결합해서 마지막 결과값을 리턴
  • 각 아이템을 결합해서 단 하나의 값으로 리턴할 때 사용합니다.
//기본 형태는 다음과 같이  구성되어있습니다.
let num = numbers.reduce(initrialREsult) { nextResult, 배열의 값 in
        return nextResult + 배열의 값
}


let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// reduce(초기값) // 초기값, // second 배열의 값
let num = numbers.reduce(0) { first, second in
    print("first = \(first) + second = \(second)")
    return first + second
}

print(num)

first = 0 + second = 1
first = 1 + second = 2
first = 3 + second = 3
first = 6 + second = 4
first = 10 + second = 5
first = 15 + second = 6
first = 21 + second = 7
first = 28 + second = 8
first = 36 + second = 9
first = 45 + second = 10
55
  • 처음 초기값이 First라는 변수에 들어오며, 이후 return 된 값은 다시 초기값으로 설정됩니다. second는 배열의 값입니다.
  • 동작은 확인해 보면 처음 사이클에서는 0 + 1, 1  + 2,  3 + 4 .... 45 + 10의 순서로 동작하게 됩니다.
// 예제 1
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

let num = numbers.reduce(100) { $0 - $1}

print(num) // 45


// 예제 2
let numString = numbers.reduce("0") { $0 + String($1) }

print(numString) // 012345678910
  • 초기값 기준으로 빼는 것도 가능합니다.
  • 문자열로 리턴하는 것도 가능합니다.

여러 개의 고차함수 활용

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

let evenSquareSum = numbers.filter { $0 % 2 == 0}
    .map { $0 * $0}
    .reduce(0) { $0 + $1 }

print(evenSquareSum) // 220
  • 고차함수를 조합해서 다양하게 사용하는 것도 가능합니다.

 

4. forEach

  • 기존 배열 등의 각 아이템을 활용해서 아이템 별로 특정 작업을 실행
  • 각 아이템을 활용해서 각각 특정 작업을 실행할 때 사용
  • forEach는 반환타입이 Void입니다.
let immutableNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


immutableNumbers.forEach {
    print($0)
}


// 실제 앱에서 사용방식
[a, b, c, d].forEach {
	view.addSubView($0)
}
  • 실제 앱에서는 위와 같이 사용하는 경우도 종종 있습니다.

5. compactMap

  • 기존 배열 등의 각 아이템을 새롭게 매핑해서 변형하되, 옵셔널 요소는 제거하고 새로울 배열을 리턴합니다.
  • map + 옵셔널 제거
  • 옵셔널 바인딩의 기능까지 내장
let numbers: [Int?] = [1, nil, nil, 3, 4, 5]

let newNumber = numbers.compactMap { $0 }

print(type(of: newNumber), newNumber) // Array<Int> [1, 3, 4, 5]

// compactMap를 사용하는것과 동일
let newNumber2 = numbers.filter { $0 != nil } .map { $0!}
print(newNumber2)

 

6. flatMap

  • 중첩된 배열의 각 배열을 새롭게 매핑해서 내부 중첩된 배열을 제거하고 리턴
  • 옵셔널을 한 단계 평탄화(Optional<Optional<T>> → Optional<T>)
  • 중첩된 배열([[T]])을 하나의 배열([T])로 변환
  • 중첩된 옵셔널(Optional<Optional<T>>)을 하나의 옵셔널(Optional<T>)로 변환
  • 비동기 스트림(Publisher<Publisher<T>>)을 단순한 스트림(Publisher<T>)으로 변환
let nestedArray = [[1,2,3], [1,2,3], [1,2,3]]

var numbers = nestedArray.flatMap { $0 }
print(numbers)


let thirdnestedArray = [[[1,2,3],[4,5,6]], [[1,2,3],[4,5,6]]]

var numbers2 = thirdnestedArray.flatMap { $0 }.flatMap { $0 }
print(numbers2)

 

  • 삼차원 배열의 경우 FlatMap을 한 번 더 사용해서 중첩을 풀 수 있습니다.
728x90