[스위프트 프로그래밍-7장] 함수

sanghee·2021년 10월 7일
0
post-thumbnail

이 글은 스위프트 프로그래밍(3판, 야곰 지음)을 읽고 간단하게 정리한 글입니다. 책에 친절한 설명과 관련 예제 코드들이 있으므로 직접 사서 읽기를 추천합니다.

7.0 소개

함수는 작업의 가장 작은 단위이자 하나의 작은 프로그램이다.

7.1 함수와 메서드

함수와 메서드는 기본적으로 같지만 상황이나 위치에 따라 다르게 부른다. 구조체, 클래스, 열거형 등 특정 타입에 연관되어 있는 함수를 메서드, 모듈 전체에서 전역적으로 사용하는 함수를 함수라고 부른다.

7.2 함수의 정의와 호출

스위프트의 함수는 조건문이나 반복문과 달리 함수에서는 소괄호를 생략할 수 없다. 재정의(오버라이드)와 중복 정의(오버로드)를 모두 지원한다. 따라서 매개변수의 타입이 다르거나 개수가 다르다면 같은 이름의 함수를 여러 개 만들 수 있다.

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

함수는 함수의 이름, 매개 변수(파라미터), 반환 타입 등으로 정의한다. 함수를 정의하는 키워드는 func이며 반환을 위한 키워드는 return이다.

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

func 함수 이름(매개 변수: 매개 변수 타입, ...) -> 반환 타입 {
    실행 구문
    return 반환 값
}
func 함수 이름(매개 변수: 매개 변수 타입, ...) -> 반환 타입 {
    반환 값
}

매개변수와 전달인자?

매개변수는 함수를 정의할 때 외부로부터 받아들이는 전달 값의 이름이다. 전달인자 혹은 인자는 함수를 실제로 호출할 때 전달하는 값을 의미한다. hello(name: "Lee")일때 매개변수는 name, 전달인자는 "Lee"이다.

7.2.2 매개변수

스위프트의 함수가 매개변수에 따라 모양과 기능이 어떻게 달라지는지 알아보자.

매개변수가 없거나 많거나

함수에 매개변수가 없다면 공란으로 비워둔다.

func hello() -> String {
    return "Hello"
}

hello()

매개변수가 여러 개일때는 쉼표(,)로 구분한다. 함수를 호출할 때에는 매개변수 이름과 그 옆에 콜론(:)을 붙이고, 전달인자를 보낸다.

func hello(myName: String, yourName: String) -> String {
    return "Hello, \(yourName)! Im \(myName)."
}

hello(myName: "Lee", yourName: "Hee") // "Hello, Hee! Im Lee."

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

전달인자 이레이블도 지정할 수 있다. hello라는 함수는 from과 to라는 전달인자 레이블이 있으며 myName과 yourName이라는 매개변수 이름이 있다.

func hello(from myName: String, to yourName: String) -> String {
    return "Hello, \(yourName)! Im \(myName)."
}

hello(from: "Lee", to: "Hee") // "Hello, Hee! Im Lee."

전달 인자 레이블 생략

func hello(_ myName: String, _ yourName: String) -> String {
    return "Hello, \(yourName)! Im \(myName)."
}

hello("Lee", "Hee") // "Hello, Hee! Im Lee."

매개변수 기본값

매개변수에 기본값을 지정할 수도 있다. 기본값이 없는 매개변수는 앞에 적는데, 함수에 중요한 값을 전달할 가능성이 높기 때문이다.

func hello(_ myName: String, _ yourName: String = "Unknown") -> String {
    return "Hello, \(yourName)! Im \(myName)."
}

hello("Lee") // "Hello, Unknown! Im Lee."

가변 매개변수와 입출력 매개변수

매개변수로 몇 개의 값이 들어올지 모를 때 가변 매개변수를 사용할 수 있다. 0개 이상의 값을 받을 수 있다. 함수마다 하나만 가질 수 있다.

func hello(names: String...) -> String {
    return names.map({ "Hello, " + $0 + "! " }).reduce("", +)
}

hello(names: "A", "B", "C") // "Hello, A! Hello, B! Hello, C! "

참조를 전달하는 입출력 매개변수

함수의 전달인자로 값을 전달할 때는 보통 값을 복사해서 전달한다. 값이 아닌 참조를 전달하려면 입출력 매개변수를 사용한다. 이 방법은 함수 외부의 값에 어떤 영향을 줄 수 있지 모르기 때문에 함수형 프로그래밍 패러다임에서는 지양한다. 입출력 매개변수의 전달 순서는 다음과 같다.

  1. 함수를 호출할 때, 전달인자의 값을 복사한다.
  2. 해당 전달인자의 값을 변경하면, 1에서 복사한 것을 함수 내부에서 변경한다.
  3. 함수를 반환하는 시점에 2에서 변경된 값을 원래의 매개변수에 할당한다.

inout 매개변수로 전달될 변수 또는 상수 앞에 앰퍼샌드(&)를 붙여서 표현한다.

var name: String = "Lee"

func hello(name: inout String) -> String {
    name = "Kim"
    return "Hello, \(name)!"
}

print(name) // Lee
hello(name: &name) // Hello, Kim!
print(name) // Kim

7.2.3 반환이 없는 함수

함수에 반환 타입이 없다면 없음을 의미하는 Void를 표기하거나 반환 타입 표현을 생략할 수 있다.

func hello() -> Void {
    print("Hello")
}

hello() // Hello
func hello() {
    print("Hello")
}

hello() // Hello

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

스위프트의 함수는 일급 객체이므로 하나의 데이터 타입으로 사용할 수 있다.

(매개변수 타입의 나열) -> 반환 타입
func hello(myName: String, yourName: String) -> String {
    return "Hello, \(yourName)! Im \(myName)."
}

// 함수 타입: (String, String) -> String
func hello() {
    print("Hello")
}

// 함수 타입: Void -> Void

함수의 축약 표현

함수를 간략히 표현하고자 할때 hello(myName, yourName)과 같이 이름과 매개벼수 개수 등을 통해 표현할 수 있다.

함수의 타입 사용

전달인자 레이블은 함수 타입의 구성요소가 아니므로 함수 타입을 작성할 때는 전달인자 레이블을 써줄 수 없다.

typealias HelloNames = (String, String) -> String

func hello(_ myName: String, _ yourName: String) -> String {
    return "Hello, \(yourName)! Im \(myName)."
}

var newFuntion: HelloNames = hello

newFuntion("Lee", "Hee") // "Hello, Hee! Im Lee."

7.3 중첩 함수

스위프트는 데이터 타입의 중첩이 자유롭다. 열거형 안에 열거형이 들어가거나, 클래스 안에 클래스가 들어올 수 있다. 함수 안에 함수가 들어올 수도 있다. 단 정의된 상위 블록 내부에서만 사용이 가능하다.

typealias MoveFunc = (Int) -> Int

func functionForMove(_ shouldGoLeft: Bool) -> MoveFunc {
    func goLeft(_ position: Int) -> Int {
        return position - 1
    }
    func goRight(_ position: Int) -> Int {
        return position + 1
    }
    
    return shouldGoLeft ? goLeft : goRight
}

var position = -4
let moveToZero: MoveFunc = functionForMove(position > 0) // goRight 함수가 된다.

while position < 0 {
    print(position) // -4 -3 -2 -1
    position = moveToZero(position)
}

print(position) // 0

7.4 종료되지 않는 함수

스위프트에는 종료되지 않는 함수가 있다. 정상적으로 끝나지 않음을 의미한다. 이를 비반환 함수 또는 비반환 메서드라고 한다. 오류를 던지거나 중대한 시스템 오류를 보고하는 등의 일을 하고 프로세스를 종료한다. 비반환 타입(메서드)는 반환 타입을 Never라고 명시하면 된다.

func crashAndBurn() -> Never {
    fatalError("에러 발생!")
}

7.5 반환 값을 무시할 수 있는 함수

가끔 반환 값이 꼭 필요하지 않는 경우도 있다. 그럴 때는 함수의 반환값을 무시해도 된다는 @discardableResult 선언 속성을 사용한다. 아래의 코드에서 반환값을 사용하지 않았기에 컴파일러가 경고를 표시할 수 있다. 그럴 때는 @discardableResult 선언 속성을 사용한다.

@discardableResult func hello(_ name: String) -> String {
    print("Hello, \(name)")
    return "Hello, \(name)"
}

hello("Lee")
profile
👩‍💻

0개의 댓글