Swift 9. 함수

 Swift 9. 함수




1. 함수


함수의 대부분은 작업의 가장 작은 단위 이자 하나의 작은 프로그램

하나의 프로그램은 하나의 큰 함수 

함수는 프로그램을 이루는 주된 요소


함수와 메소드는 기본적으로 같음 - 함수가 위치하거나 사용되는 범위 등에 따라 호칭이 달라짐

함수는 모듈 전체에서 전역적으로 사용할 수 있음

메소드는 구조체, 클래스, 열거형 등 특정 타입에 연관되어 사용하는 함수


Swift에서 함수는 다른 언어보다 훨씬 다양한 모습으로 존재

Swift에서 함수는 일급 객체이기 때문에 하나의 값으로도 사용할 수 있음






2. 함수 정의와 호출

함수를 정의할 때 소괄호 ( ( ) )는 생략할 수 없음

함수의 재정의(오버라이드)와 중복 정의(오버로드)를 모두 지원

매개변수의 타입이나 개수가 다르면 같은 이름의 함수를 여러개 만들 수 있음



- 기본적인 함수의 정의와 호출

func 키워드로 정의

함수의 이름, 매개변수. 반환타입 등을 사용해서 함수 정의

함수의 이름을 지정하고 매개변수를 소괄호 ( ( ) )로 감싸고 반환 타입을 -> 다음에 명시

실행문에서 반환을 위한 키워드는 return


func [함수 이름] ([매개변수]: [타입] ... ) -> [반환 타입]) {

실행문

}


* 예시

// 함수 정의

func hello(name: String) -> String{

    return "Hello \(name)"

}


let Kim: String = hello(name: "Kim") // 함수 호출

print(Kim)

<실행결과>

Hello Kim



함수 내부의 코드가 한 줄 이고 결과값의 타입이 함수의 반환 타입과 같다면 return 키워드를 생략할 수 있음


* 예시 - return 키워드를 생략한 함수 정의

// 함수 정의

func hello(name: String) -> String{

    "Hello \(name)"

}


let Kim: String = hello(name: "Kim") // 함수 호출

print(Kim)

<실행결과>

Hello Kim




- 매개변수

매개변수 : 함수를 정의할 때 외부로 부터 받아들이는 전달값의 이름

전달인자, 인자(Argument) : 함수를 실제로 호출할 때 전달되는 값

ex) 매개변수 : name, 전달인자 : "Kim"



Swift의 함수는 매개변수를 어떻게 정의하냐에 따라 모습이 크게 달라짐


* 매개변수가 없는 함수

함수에 매개변수가 필요 없다면 매개변수 위치를 공란으로 비워둠


* 예시

// 매개변수가 없는 함수 정의

func hello() -> String{

    "Hello World"

}


print(hello())

<실행결과>

Hello World



* 매개변수가 여러 개인 함수

매개변수가 여러 개 필요한 함수를 정의할 때는 쉼표(,)로 매개변수를 구분

함수를 호출할 때 매개변수의 이름을 붙여주고 콜론(:)을 적고 전달인자 작성


* 예시

// 매개변수가 여러 개인 함수 정의

func sum(num1: Int, num2: Int) -> Int{

    return num1 + num2

}


print(sum(num1: 5, num2: 7))

<실행결과>

12



* 매개변수 이름으로 Swift의 키워드를 사용할 수 없지만 키워드 앞 뒤에 '₩'(탭키 위의 특수 기호)를 붙여주면 키워드도 매개변수 이름으로 사용할 수 있음

func sum(`Int`: Int, num2: Int) -> Int{

    return Int + num2

}


print(sum(Int: 5, num2: 7))




* 매개변수 이름과 전달인자 레이블

함수를 정의할 때 매개변수를 정의하면 전달인자 레이블을 매개변수와 같은 이름으로 사용

-> 전달인자 레이블을 따로 지정해 줄 수도 있음 => 함수 외부에서 매개변수의 역할을 좀 더 명확하게 알 수 있음

함수 내부에서는 매개변수를 사용하고 함수 외부에서는 전달인자 레이블 사용

매개변수 이름 앞에 한 칸을 띄운 후 전달인자 레이블 작성


func [함수 이름] ([전달인자 레이블] [매개변수 이름]: [매개변수 타입] ...) -> 반환 타입


* 예시

// 전달인자 레이블을 지정하는 함수 정의

func letter(from myName: String, to yourName: String) -> String {

    return "\(yourName) 에게 \(myName)가(이)"

}


print(letter(from: "Lee", to: "Kim"))

<실행결과>

Kim 에게 Lee가(이)



* 전달인자 레이블을 사용하지 않으려면 와일드카드 식별자(_)를 사용


* 예시 

// 전달인자 레이블 대신 와일드카드 식별자 사용 함수 정의

func letter(_ myName: String, _ yourName: String) -> String {

    return "\(yourName) 에게 \(myName)가(이)"

}


print(letter("Lee", "Kim")) // 전달인지 레이블 없이 값만 입력

<실행결과>

Kim 에게 Lee가(이)



* 전달인자가 다르면 이름이 같아도 다른 함수로 인식 -> 함수 중복 정의 가능

(매개변수의 이름은 달라도 같은 함수로 인식해서 에러 발생)


* 예시

// 이름은 같지만 전달인자 레이블이 다른 두 함수 정의

func letter(_ myName: String, _ yourName: String) -> String {

    return "\(yourName) 에게 \(myName)가(이)"

}


func letter(from myName: String, to yourName: String) -> String {

    return "To \(yourName), From \(myName)"

}


// 전달인자가 다르면 이름이 같아도 다른 함수로 인식

print(letter("Lee", "Kim"))

print(letter(from: "Lee",to: "Kim"))

<실행결과>

Kim 에게 Lee가(이)

To Kim, From Lee



* 매개변수의 기본값

매개변수마다 기본값을 지정할 수 있음

매개변수가 전달되지 않으면 기본값 사용

매개변수를 지정할 때 변수를 초기화 하는 것 처럼 기본값 지정 ex) a: Int = [기본값]

매개변수 기본값이 설정된 함수는 함수를 중복 정의한 것 처럼 사용 가능

기본값이 없는 매개변수를 기본값이 있는 매개변수 앞에 사용


* 예시

func hello (_ name: String, _ time: Int = 3) -> String {//time 의 기본값 3으로 지정

    var result: String = ""

    

    for _ in 1...time{

        result += "hello \(name) "

    }

    

    return result

}


print(hello("Kim")) // time에 전달인자가 없으면 기본값 3으로 지정

print(hello("Lee", 2))

<실행결과>

hello Kim hello Kim hello Kim 

hello Lee hello Lee 




* 가변 매개변수

매개변수로 몇개의 값이 들어올 지 모를 때 가면 매개변수 사용

0개 이상(0개 포함)의 값을 받아올 수 있음

매개변수의 자료형 뒤에 '...'을 붙여서 사용

가변 매개변수로 들어온 인자 값은 배열처럼 사용할 수 있음

함수마다 가변 매개변수는 하나씩만 사용할 수 있음


* 예시

// 가변 매개변수 사용 함수 정의

func hello (_ names: String...) -> String {

    var result: String = "hello "

    

    for name in names{

        result += (name + " ")

    }

    

    return result

}


print(hello("Kim", "lee", "Choi"))

<실행결과>

hello Kim lee Choi 




* 참조 전달 함수

함수의 전달인자로 값을 전달할 때는 보통 값을 복사해서 전달

값이 아닌 참조를 전달하려면 입출력 매개변수 사용

함수를 정의할 때 매개변수의 자료형 앞에 'inout' 키워드 사용해서 표현

데이터의 참조를 전달하면 함수 내부에서 참조하여 원본 데이터 변경 (C언어의 포인터와 유사)

함수 외부의 값에 어떤 영향을 줄 지 모르기 때문에 함수형 프로그래밍 패러다임에서는 지양

iOS, macOS 등의 애플 프레임워크에서는 객체지향 프로그래밍 패러다임을 사용하므로 유용하게 사용할 수 있음


입출력 매개변수의 전달 순서

1) 함수를 호출할 때 전달인자의 값을 복사

2) 해당 전달인자의 값을 변경하면 복사한 값을 함수 내부에서 변경

3) 함수를 반환하는 시점에서 변경된 값을 원래 매개변수에 할당



연산 프로퍼티나 감시자가 있는 프로퍼티가 입출력 매개변수로 전달되면 함수 호출 시점에 그 프로퍼티의 접근자가 호출되고 반환 시점네 프로퍼티의 설정자가 호출


함수 호출 시 inout 매개변수로 전달될 변수나 상수 앞에 앰퍼샌드(&)를 붙여서 표현

입출력 매개변수는 매개변수 기본값을 가질 수 없음

입출력 매개변수는 가변 매개변수로 사용될 수 없음

상수는 변경될 수 없으므로 입출력 매개변수로 사용될 수 없음

입출력 매개변수를 잘못 사용하면 메모리 안전을 위협하기 때문에 사용에 몇몇 제약이 있음


* 예시

var a: Int = 7


// 값 전달 함수 정의

func plus(_ num: Int) -> Int{

    var number = num

    number += 1

    return number

}

// 참조 전달 함수 정의

func referencePlus(_ num: inout Int) -> Int{

    num += 1

    return num

}


print(plus(a))

print("num : \(a)")


print(referencePlus(&a))

print("num : \(a)")

<실행결과>

8

num : 7

8

num : 8

-> 참조를 전달한 함수의 실행 결과는 원본 데이터의 값을 변경





- 반환이 없는 함수

함수는 특정 연산을 실행한 후 결과값을 반환

값의 반환이 굳이 필요하지 않은 경우 반환값이 없는 함수를 만들어줌

-> 함수의 반환타입을 Void 로 하거나 아예 반환타입을 지정하지 않음



* 예시

// 반환값이 없는 함수 정의

func hello() {

    print("Hello!")

}

// 반환값이 없는 Void형 함수 정의

func world() -> Void{

    print("World!")

}


hello()

world()

<실행결과>

Hello!

World!





* 함수의 축약 표현

함수의 이름과 매개변수의 이름만으로 표현

ex)

fucn hello(name: String, time: Int) -> String

=> hello(name: time:)







- 데이터 타입으로서의 함수

Swift 의 함수는 일급 객체이므로 하나의 데이터 타입으로 사용할 수 있음

함수는 매개변수 타입과 반환 타입으로 구성된 하나의 타입으로 사용(정의) 할 수 있음


함수를 하나의 데이터 타입으로 나타내는 방법

([매개변수 타입의 나열]) -> [반환 타입]


ex)

func hello(name: String, time: Int) -> String

=> 함수의 타입 : (String, Int) -> String)

func sum(num1: Int, num2: Int) -> Int

=> 함수의 타입 : (Int, Int) -> Int


매개변수나 반환 타입이 없다면 Void 키워드나 빈 소괄호 (( ))의 묶음으로 표현

ex) - 모두 같은 표현

(Void) -> void

() -> Void

() -> ()


함수를 데이터 타입으로 이용하면 함수를 전달인자로 받을 수도 있고 반환값으로 돌려줄 수도 있음


* 예시

// 함수를 타입으로 이용해서 타입 별칭 지정

typealias Cal = (Int, Int) -> Int


// 함수 정의

func add(_ a: Int, _ b: Int) -> Int{

    return a + b

}


func multi(_ a: Int, _ b: Int) -> Int{

    return a * b

}


// 함수를 변수의 데이터 타입으로 이용

var mathFunction: Cal = add


print(mathFunction(5, 7))


mathFunction = multi

print(mathFunction(3, 4))

<실행결과>

12

12

-> mathFunction 변수에 add함수와 multi 함수가 번갈아가면서 할당되로 mathFunction이라는 이름으로 호출



* 예시 - 전달인자로 함수 전달

// 함수를 타입으로 이용해서 타입 별칭 지정

typealias Cal = (Int, Int) -> Int


// 함수 정의

func add(_ a: Int, _ b: Int) -> Int{

    return a + b

}

// 함수를 전달인자로 전달하는 함수 정의

func mathFunc(_  mathFunction: Cal, _ a: Int, _ b: Int) -> Int{

    return mathFunction(a, b)

}


print(mathFunc(add, 5, 6)) // 함수를 전달인자로 전달

<실행결과>

11




* 예시 - 반환값으로 함수 반환

// 함수를 타입으로 이용해서 타입 별칭 지정

typealias Cal = (Int, Int) -> Int


// 함수 정의

func add(_ a: Int, _ b: Int) -> Int{

    return a + b

}


func multi(_ a: Int, _ b: Int) -> Int{

    return a * b

}


// 함수를 전달인자로 전달하는 함수 정의

func mathFunc(_  mathFunction: Cal, _ a: Int, _ b: Int) -> Int{

    return mathFunction(a, b)

}


// 함수를 반환하는 함수 정의 (함수 선택 함수)

func chooseMathFunc(_  toAdd: Bool) -> Cal{

    return toAdd ? add : multi

}


print(mathFunc(chooseMathFunc(false), 5, 6))

<실행결과>

30



* 전달인자 레이블은 함수 타입의 구성요소가 아니기 때문에 함수 타입을 작성할 때는 전달인자 레이블을 써줄 수 없음

ex)








3. 중첩 함수

Swift는 데이터 타입의 중첩이 자유로움

열거형 안에 열거형이 들어갈 수 있고 클래스 안에 클래스가 들어갈 수 있음

함수의 중첩은 함수 안에 함수를 넣는 것

함수 안에 함수로 구현된 중첩 함수는 상위 함수의 몸통 블록 내부에서만 함수를 사용할 수 있음

-> 중첩 함수를 담은 함수가 중첩 함수를 반환하면 밖에서도 사용 가능


* 예시 - 중첩 함수를 이용하지 않은 경우

// 0으로 가까워지는 프로그램


// 플러스, 마이너스 함수

func plus(_ currentNum: Int) -> Int{

    return currentNum + 1

}

func minus(_ currentNum: Int) -> Int{

    return currentNum - 1

}


// 연산 방향 선택 함수 (함수를 반환하는 함수)

func Cal(_ isPlus: Bool) -> (Int) -> Int{

    return isPlus ? plus : minus

}



var current: Int = 5 // 현재 숫자


// 연산 방향에 따라 연산 함수를 저장할 상수

let move: (Int) -> Int = Cal(current<0)


while(current != 0){

    print(current)

    current = move(current)

}

print("0 도착")



=> 중첩 함수를 이용하는 경우

// 0으로 가까워지는 프로그램


// 연산 방향 선택 함수 (함수를 반환하는 함수)

func Cal(_ isPlus: Bool) -> (Int) -> Int{

    // 플러스, 마이너스 함수 정의 (함수 안에 함수 정의)

    func plus(_ currentNum: Int) -> Int{

        return currentNum + 1

    }

    func minus(_ currentNum: Int) -> Int{

        return currentNum - 1

    }

    

    return isPlus ? plus : minus

}



var current: Int = 5 // 현재 숫자


// 연산 방향에 따라 연산 함수를 저장할 상수

let move: (Int) -> Int = Cal(current<0)


while(current != 0){

    print(current)

    current = move(current)

}

print("0 도착")

<실행결과>

5

4

3

2

1

0 도착




4. 종료되지 않는 함수

종료(return) 되지 않음

정상적으로 끝나지 않는 함수 -> 비반환 함수 또는 비반환 메소드

정상적으로 끝날 수 없음

비반환 함수를 실행하면 프로세스 동작은 종료됨

오류를 던지거나 중대한 시스템 오류를 보고하는 등의 일을 하고 프로세스 실행 종료

어디서든 호출이 가능하고 guard 문의 else 블록에서도 호출 가능

비반환 메소드를 재정의는 할 수 있지만 비반환 타입이라는 것을 변결할 수는 없음

반환 타입을 Never로 명시


* 예시




<실행결과>

__lldb_expr_76/MyPlayground.playground:4: Fatal error: Something very bad happened

-> 프로세스 종료하고 오류 보고

(fatalError() : 메시지 내용 출력하고 무조건 프로그램 종료)



* 예시 - guard 문에서 비반환 함수 호출

func crushAndBurn() -> Never{

    fatalError("Something very bad happened")

}



func someFunc(isAllIsWell: Bool){

    guard isAllIsWell else{

        print("마을에 도둑이 들었습니다.")

        crushAndBurn()

    }

    print("All is Well")

}


someFunc(isAllIsWell: true)

someFunc(isAllIsWell: false)

<실행결과>

All is Well

마을에 도둑이 들었습니다.

__lldb_expr_82/MyPlayground.playground:4: Fatal error: Something very bad happened





5. 반환 값을 무시할 수 있는 함수

함수의 반환값이 꼭 필요하지 않은 경우 프로그래머가 의도적으로 함수의 반환값을 사용하지 않으면 컴파일러는 함수의 결과값을 사용하지 않았다고 경고 발생

함수의 반환값을 무시해도 된다는 @discardableResult 선언 속성을 사용

func 키워드 앞에 @discardableResult 선언 속성 작성

@discardableResult 선언 속성은 Swift 표준 라이브러리에서도 종종 사용


* 예시

@discardableResult func hi(_ name: String) -> String{

    print("Hi \(name)")

    return "Hi \(name)"

}


hi("Lee")

<실행결과>

Hi Lee

=> 반환값을 사용하지 않아도 컴파일러가 경고하지 않음







댓글

이 블로그의 인기 게시물

Spring Boot 6. 계시판 프로젝트

Spring Boot 2. Maria 데이터베이스 & Spring Data JPA

Django 10. DB 연동