본문 바로가기

iOS 앱 개발자 프로젝트/Swift 문법 정복하기

[Swift] Closure, Higher Order Function

2024년 3월 16일 토요일

 

  • 익명 함수 closurer와,
  • 다른 함수를 인자로 받거나, 함수의 결과로 함수를 반환하는 고차함수(map, filter, reduce)를 공부해 봅니다.

#1. Closure

#2. Higher Order Function (고차함수)


 

#1. Closure

 

  • 이름없는 함수 즉, 코드 블록을 말한다. ('closure'라는 단어가 참 잘 어울린다.. )
  • 상수나 변수의 참조를 캡쳐(capture)해 저장할 수 있습니다
    • 주변 환경에 있는 변수나 상수를 캡처하여 저장하고, 이를 나중에 사용할 수 있도록 하는데 이것은 클로저가 생성될 때 클로저가 참조하는 변수 또는 상수의 값에 대한 복사본을 유지하고 저장하는 메커니즘이다.
    • 값(value) 캡처: 클로저가 변수나 상수의 값을 캡처한다. 이때, 클로저 내부에서 캡처한 값이 변경되어도 원본 값은 변경되지 않는다.
    • 참조(reference) 캡처: 클로저가 변수나 상수의 참조를 캡처한다. 클로저 내에서 해당 변수나 상수를 변경하면 원본 값도 변경된다.
// 값 캡처
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var total = 0
    
    // 클로저를 반환합니다.
    let incrementer: () -> Int = {
        // total 변수를 캡처하여 저장합니다.
        total += amount
        return total
    }
    
    return incrementer
}

let incrementByTen = makeIncrementer(forIncrement: 10)

print(incrementByTen()) // total = 10, 결과: 10
print(incrementByTen()) // total = 20, 결과: 20

// 참조 캡처
class SimpleClass {
    var value: Int = 10
}

func createClosure() -> (() -> Int) {
    let instance = SimpleClass()
    
    // 참조 캡처를 사용하여 SimpleClass의 인스턴스를 캡처합니다.
    let closure: () -> Int = {
        // 클로저가 참조하는 인스턴스의 속성을 업데이트합니다.
        instance.value *= 2
        return instance.value
    }
    
    return closure
}

// 클로저 생성
let myClosure = createClosure()

print(myClosure()) // 20
print(myClosure()) // 40

// 클로저 내부에서 참조된 인스턴스의 속성을 변경하였으므로 원본에도 영향을 줍니다.
  • 클로저를 사용하는 이유는? 가장 일반적으로는 기능을 저장하기 위해서 사용한다.
  • 클로저는 비동기 처리가 필요할 때 사용할 수 있는 코드 블록이다.
  • 클로저는 클래스와 마찬가지로 참조 타입(reference type)이다.
{ (parameters) -> return type in
    // 구현 코드
}

// 함수와 클로저 비교
func pay(user: String, amount: Int) {
    // code
}

let payment = { (user: String, amount: Int) in
    // code
}
func func1(_ param: String) -> String {
    return param + "!"
}

func func2(name: String) -> String {
    return name + "***"
}

// 함수를 변수에 할당가능(변수가 함수를 가르키게 됨)
var a: (String) -> String = func1

a("안녕")

a = func2

a("hello")

// 함수(클로저)를 변수에 할당해서
let closure1 = { (param: String) -> String in         // 클로저 리터럴
    return param + "!"
}

// 사용(실행)
closure1("스티브")

 


#2. Higher Order Function (고차함수) : map, filter, reduce

 

 

☑️  map 함수

  • map 함수는 컬렉션 내부의 기존 데이터를 변형(transform)하여 새로운 컬렉션를 생성
  • 기존의 컬렉션의 요소에 대해 정의한 익명함수로 매핑한 결과를 새로운 컬렉션으로 반환
// for 문으로 구현
let num = ["1", "2", "3", "4", "5"]
var numberArray: [Int] = []
for index in num {
    if let changeToInt = Int(index) {
        numberArray.append(changeToInt)
    }
}

print(numberArray)
// [1, 2, 3, 4, 5]

// map으로 구현
let stringArray = ["1", "2", "3", "4", "5"]
numberArray = stringArray.map { 
		if let changeToInt = Int($0) {
				return changeToInt
		}
		return 0
}

/*
$0와 $1
{ } 를 익명함수인 클로저라고 합니다.
클로저의 매개변수 이름이 필요하지 않은 경우 단축 인자 이름을 활용할 수 있습니다.($0, $1)
단축 인자이름은 순서대로 $0 , $1 , $2, $3 ...으로 표현합니다.
$0 은 첫번째 인자, $1은 두번째 인자를 뜻합니다.
*/

print(numberArray)
// [1, 2, 3, 4, 5]

 

 

 

☑️  filter 함수  

  • 기존 컨테이너의 요소 중 조건에 만족하는 값에 대해 새로운 컨테이너를 만들어 반환
// for 문으로 구현
// numbers에서 짝수만 추출하기

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
var evenNumbers: [Int] = []

for number in numbers {
    if number % 2 == 0 {
        evenNumbers.append(number)
    }
}

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

// filter로 구현
// numbers에서 짝수만 추출하기

let numbers1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let evenNumbers2 = numbers1.filter { $0 % 2 == 0 }

print(evenNumbers2)
// [2, 4, 6, 8]

 

 

 

☑️  reduce 함수

  • 기존의 컨테이너의 요소에 대해 정의한 클로저로 매핑한 결과를 새로운 컨테이너로 반환
// for 문으로 구현
// 각 요소의 합 구하기

let numbers2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var sum = 0

for number in numbers2 {
    sum += number
}

print(sum)
// 55


// reduce로 구현
// 표현식1
// 각 요소의 합 구하기

let numbers3 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let sum1 = numbers3.reduce(0, +)

print(sum1)
// 55


//표현식2
// 각 요소의 합 구하기

let numbers4 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let sum2 = numbers4.reduce(0) { $0 + $1 }

print(sum2)
// 55