상세 컨텐츠

본문 제목

First-Class Object - 일급 객체로서의 함수

Swift&SwiftUI

by (방울)도마토 2024. 9. 8. 16:33

본문

일급 객체로서의 함수

1. 변수나 상수에 함수를 대입할 수 있음 

2. 함수의 반환 타입으로 함수를 사용할 수 있음 

3. 함수의 인자값으로 함수를 사용할 수 있음 

 

 

1. 변수나 상수에 함수를 대입할 수 있음

- 함수의 결과값을 대입하는 것이 아니라 함수 자체를 대입할 수 있음!

    - 함수 자체를 변수에 할당하면 변수도 함수처럼 인자값을 받아 실행할 수 있고, 값을 반환할 수도 있음

func greeting(name: String) -> String {
    return "Hello " + name
}

let hello = greeting(name:)		// 'hello'라는 변수에 함수를 할당 

print(hello("Soy"))		// Hello Soy

 

 

 

[참조] 함수 타입(Function Types) 

- 변수에 함수를 대입하면 함수 타입이 됨!

let a: Int 	// 정수 타입 
let b: String 	// 문자열 타입 
let hello: (String) -> String	// 문자열을 입력받아 문자열을 return하는 함수 타입

// 함수를 상수에 할당하는 구문 
let hello: (String) -> String = greeting
// 다양한 함수 타입 
// 1. 튜플이 반환값인 경우 
(Int, String) -> String 

// 2. 인자값이 없는 경우 
() -> String

// 3. 반환값이 없는 경우 
String -> ()

// 4. 인자값, 반환값이 모두 없는 경우 
() -> ()

 

 

 

 

[참조] 함수의 이름은 같지만, 함수의 식별자가 다른 함수를 상수에 할당하는 경우

함수의 이름: greeting

함수의 식별자: greeting(name:), greeting(today:name:)

 

error: 함수 'greeting' 의 사용이 모호하다

 

* 해결방안

// (1) 타입 어노테이션을 통해 입력받을 함수의 타입을 지정 
let hello: (Int, String) -> String = greeting

// (2) 함수의 식별값을 통해 입력받을 정확한 함수 지정 
let hello = greeting(today:name:)

 

 

 

 

2. 함수의 반환 타입으로 함수를 사용할 수 있음 

func greeting(name: String) -> String {
    return "Hello " + name
}

func returnGreeting() -> (String) -> String {
    // 반환값으로 (String) -> String 타입의 함수 greeting(name:)을 반환함!
    return greeting(name:)
}

returnGreeting()("Soy")

 

 

// 조금 복잡한 형태의 함수 반환 형식 예제
func plus(a: Int, b: Int) -> Int { return a + b }

func minus(a: Int, b: Int) -> Int { return a - b }

func times(a: Int, b: Int) -> Int { return a * b }

func divide(a: Int, b: Int) -> Int {
    guard b != 0 else {
        return 0
    }
    
    return a / b
}


// String을 입력받고, (Int, Int) -> Int 타입을 반환!
func calc(_ operand: String) -> (Int, Int) -> Int {
    switch operand {
    case "+":
        return plus(a:b:)
        
    case "-":
        return minus(a:b:)
        
    case "*":
        return times(a:b:)
        
    case "/":
        return divide(a:b:)
        
    default:
        return plus(a:b:)
    }
}

calc("+")
calc("+")(3, 4)     // plus(a: 3, b: 4)

// 가독성을 위해 단계적으로 표시하는 것이 좋음
let c = calc("*")
c(3, 4)     // times(a: 3, b: 4)

 

 

1. calc의 타입이 (Int, Int) → Int 임을 확인할 수 있음 2. 상수 c에서 (Int, Int) 를 입력할 것을 요구받고 있음
반환값으로 호출된 함수를 사용할 때는 인자 레이블을 사용하지 않음!

 

 

 

 

3. 함수의 인자값으로 함수를 사용할 수 있음 

func increase(param: Int) -> Int {
    return param + 1
}

// broker: 중개 역할을 수행하는 함수 
// argument function은 정수를 입력받고 정수를 반환하는 함수 타입을 입력값으로 받음 
// -> 함수를 인자값으로 사용할 수 있다!
func broker(base: Int, function: (Int) -> Int) -> Int {
    return function(base)
}

broker(base: 1, function: increase)		// 2
func greeting(name: String) -> String {
    return "Hello " + name
}

func anotherGreeting(action: () -> Void) {
    // anotherGreeting의 argument -> action
    action()    // action은 전달받은 함수를 실행함!
}

anotherGreeting {
    // action을 통해 전달받은 함수 greeting의 return 값 출력!
    print(greeting(name: "Soy"))
}

 

 

[참조] 콜백 함수(Callback Function)

- 다른 함수에 인자로 전달되어 특정 작업이 완료된 후 호출되는 함수

→ 어떤 함수가 완료된 후에 실행되어야 하는 작업을 미리 정의해 놓은 함수 

 

* 주요 사용 사례 

1. 비동기적 작업 처리: 네트워크 요청, 파일 읽기/쓰기, 타이머 등 시간이 걸리는 작업을 처리 후 콜백 함수 호출 

2. 이벤트 기반 프로그래밍: UI에서 버튼 클릭, 화면 전환 등의 이벤트 발생시 해당 이벤트 처리 함수로 콜백 함수 사용할 수 있음

 

// () -> Void 타입
func successThrough() {
    print("Success Computing")
}

// () -> Void 타입
func failThrough() {
    print("Error: error!")
}

// argument: base - Int 타입, 
// success(내부 매개변수: sCallback) - '() -> Void' 타입, 
// fail(내부 매개변수: fCallback) - '() -> Void' 타입
func divide(base: Int, success sCallback: () -> Void, fail fCallback: () -> Void) -> Int {
    
    // 연산과정에 실패했을 경우 
    guard base != 0 else {
        fCallback()     // fail에 할당된 함수를 실행함
        return 0
    }
    
    // defer: 코드의 흐름과 상관없이 가장 마지막에 실행되는 블록, 지연 블록
    // 연산과정에 성공했을 경우
    defer {
        sCallback()     // success에 할당된 함수를 실행함
    }
    
    return 100 / base
}

divideCallback(base: 5, success: successThrough, fail: failThrough)		// 20

 

연산과정에 성공했을 경우의 함수(successThrough)를 호출, 실행 → 반환

 

[참조] defer 블록

1. 작성된 위치와 순서에 상관없이 함수가 종료되기 직전에 실행 

2. defer 블록을 읽기 전에 함수의 실행이 종료될 경우, defer 블록은 실행되지 않음

3. 하나의 함수나 메소드 내에서 defer 블록을 여러 번 사용할 수 있음 → 가장 마지막에 작성된 defer 블록부터 역순으로 실행 

4. 중첩해서 사용가능 → 바깥쪽 defer 블록부터 실행되며 가장 안쪽에 있는 defer 블록은 가장 마지막에 실행 

⇒ 함수가 연산을 처리하는 과정에 영향을 끼치지 않으면서 실행해야 할 다른 내용이 있거나, 함수를 종료하기 직전에 정리해야 하는 변수나 상수값을 처리하는 용도 

 

 

// 비동기 작업에서의 예시

// 데이터 로드를 시뮬레이션하는 함수
func loadData(completion: @escaping (String) -> Void) {
    print("데이터를 불러오는 중입니다...")
    
    // 2초 후에 데이터 로드 완료 시 실행되는 코드
    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
        let data = "불러온 데이터"
        completion(data) // 콜백 함수 호출
    }
}

// 결과값을 프린트하는 함수 
func printResult(result: String) {
    print(result)
}

// 함수를 인자값으로 넘기는 경우
loadData(completion: printResult)

// 클로저로 표현하는 경우
loadData { result in
    print("콜백에서 받은 결과: \(result)")
}

 

 

 

 

관련글 더보기

댓글 영역