Swift

[Swift] Functions(함수) 정리

Hatchling.dev 2023. 3. 7. 16:34

(편의상 편한 말투로 작성하는 점 이해 부탁드립니다.😅)

(부정확한 정보가 있을 수 있습니다. 지적 환영🤩)

 

이번에는 Functions(이하 함수)에 대해 알아보겠습니당~

 

Functions란 무엇일까??

재사용 가능한 코드 뭉치입니다. 작업의 가장 단위이면서 코드의 집합이라고 이해하면 쉽습니다!

더보기

Method는 [class, struct, enum]에 속한 함수를 말한다.

https://woo0dev.tistory.com/2에서 설명한 것처럼 Swift는 다른 언어들의 장단점을 참고하여 만든 언어라고 설명했듯이 Swift의 함수는 인자가 없는 C언어 방식과 전역 인자 이름을 가지는 Objective-C언어의 방식 모두 표현할 수 있습니다!!! (깨알같은 편리함?😄)

 

함수의 정의와 호출

모든 함수는 이름을 가지고 있는데 이름은 어떤 작업을 하는 함수인지 설명하는 가장 좋은 방법입니다. 그렇기 때문에 함수 이름에 따라 가독성이 좋을 수도, 나쁠 수도 있으니 정말 잘 지어야 합니다...(변수명, 함수명 등 이름 짓는게 가장 어려운 사람? Me!!!😭)

함수의 이름과 함께 함수 인자에 맞는 값을 넘겨 호출하는 방식으로 사용합니다! 함수의 argument는 항상 함수의 파라미터 리스트의 순서와 일치해야 합니다.

func 함수명(파라미터명: 타입) -> 반환타입 {
    // Code
    return 반환 값
}

그럼 가장 기본적인 함수를 만들어 봅시다.

func addAge(age: Int) -> Int {
    let new_age = age + 1
    return new_age
}

print(addAge(age: 20)) // 21

함수를 선언하기 위해서는 func 키워드를 붙여주면 된다! 함수 이름은 LowerCamelCase로 시작은 동사원형으로 시작하는 것이 좋습니다. 조금 길어지더라도 명확하게!!!!★★★  (Swift Naming Convention은 다음에 포스팅할 예정!)

위 함수는 파라미터에 현재 나이를 전달하면 1살 먹은 나이를 반환하는 함수입니다.

 

함수 인자와 반환 값

위에서 설명했듯이 Swift의 함수는 극도로 유연합니다. 이름 없는 인자를 사용하는 것부터 명시적 인자, 다른 인자 옵션을 가지는 복잡한 함수까지 정의할 수 있습니다.

 

매개변수가 없는 함수

인자가 없더라도 괄호는 필수!

func greet() -> String {
    let message = "안녕하세요!"
}

print(greet()) // 안녕하세요!

다중 매개변수를 가진 함수

여러 개의 매개변수를 전달할 수 있습니다. 매개변수 구분은 콤마(,)를 사용합니다.

func makeMessage(name: String, ment: String) -> String {
    let message = name + ": " + ment
    return message
}

print(makeMessage(name: "Hatchling", ment: "안녕하세요!")) // Hatchling: 안녕하세요!

반환 값이 없는 함수

반환 값이 필수는 아닙니다. 아래 예시처럼 arrow(->)와 반환타입을 쓰지 않고 함수를 만들 수 있습니다.

func printMessage(name: String, ment: String) {
    print(name + ": " + ment)
}

printMessage(name: "Hatchling", ment: "안녕하세요!") // Hatchling: 안녕하세요!

다중 반환 값을 가진 함수

여러 개의 반환 값을 가진 함수를 만들 수 있습니다. 아래 예시처럼 반환타입을 괄호와 콤마(,)를 활용하여 작성하면 됩니다.

func calculatePlusAndMinus(first: Int, second: Int) -> (plus: Int, minus: Int) {
    let plus = first + second
    let minus = first - second
    return (plus, minus)
}

print(calculatePlusAndMinus(first: 10, second: 5).plus) // 15
print(calculatePlusAndMinus(first: 10, second: 5).minus) // 5

옵셔널을 반환하는 함수

옵셔널이란 값이 있을 수도, 없을 수도 있다는 뜻입니다. 보통 다른 언어에서 함수의 리턴을 명시하면 꼭 리턴 값이 있어야하는 경우도 있는데 Swift는 옵셔널을 통해 리턴 값이 있을 수도, 없을 수도 있는 함수를 작성할 수 있습니다.

아래 예시는 이름과 나이가 쌍으로 되어있는 Dictionary에서 특정 이름의 나이를 찾아 반환하는 함수입니다. Dictionary에 해당 이름이 있다면 그 사람의 나이를 반환(옵셔널) 하지만 그런 이름이 없다면 nil을 리턴합니다.

func checkPersonAge(name: String) -> Int? {
    let people = ["Hatchling": 20]
    return people[name]
}

print(checkPersonAge(name: "Hatchling")) // Optional(20)
print(checkPersonAge(name: "해츨링")) // nil

암묵적으로 반환하는 함수

Swift의 함수는 함수 내부에서 하나의 표현식으로만 작성되어 있다면 암묵적으로 해당 표현식을 리턴합니다.

func makeMessage(name: String, ment: String) -> String {
    name + ": " + ment
}

print(makeMessage(name: "Hatchling", ment: "안녕하세요!")) // Hatchling: 안녕하세요!

외부 인자 이름 지정 함수

아래 함수에서 to는 argumentLabel은 함수를 호출할 때 사용하는 이름이고 name은 parameterName으로 함수 내부에서 사용됩니다. 이처럼 argumentLabel과 parameterName을 지정하여 의미전달을 명확히 할 수 있습니다.

func makeMessage(to name: String, ment: String) -> String {
    return name + ": " + ment
}

print(makeMessage(to: "Hatchling", ment: "안녕하세요!")) // Hatchling: 안녕하세요!

argumentLabel 생략 함수

언더바(_)를 사용하면 argymentLabel을 생략할 수 있습니다. 함수를 호출할 때 해당 매개변수 이름을 쓰지 않아도 되기 때문에 코드는 짧아지고 깔끔해보일 수 있지만 어떤 매개변수를 넣어야하는지 혼동이 올 수 있기 때문에 무분별한 사용은 가독성을 해칠 수 있을 것 같습니다...

func makeMessage(to name: String, _ ment: String) -> String {
    return name + ": " + ment
}

print(makeMessage(to: "Hatchling", "안녕하세요!")) // Hatchling: 안녕하세요!

기본 값을 가진 매개변수를 전달하는 함수

매개변수를 전달할 때 아래처럼 값을 넣으면 호출할 때 해당 매개변수에 값을 전달하지 않아도 됩니다. 물론 함수를 호출 할 때 매개변수에 다른 값을 전달하는 것도 OK!

func makeMessage(to name: String, ment: String = "안녕하세요!") -> String {
    return name + ": " + ment
}

print(makeMessage(to: "Hatchling")) // Hatchling: 안녕하세요!

가변 매개변수를 가진 함수

함수를 호출할 때 전달하는 매개변수 개수만큼 가질 수 있는 함수입니다. 콤마(,)를 사용하여 하나의 매개변수에 여러 값들을 넣을 수 있습니다. 하지만 가변 매개변수가 여러개일 때 매개변수도 콤마(,)로 구분하고 가변 매개변수의 값들도 콤마(,)로 구분할 때 argumentLabel이 없다면 구분이 안되겠죠?? 그래서 두 번째 가변 매개변수 부터는 argumentLabel이 필수입니다~

func makeMessage(name: String..., ment: String = "안녕하세요!") -> String {
    var people = ""
    for i in 0..<name.count {
        if i < name.count-1 {
            people += name[i] + "님, "
        } else {
            people += name[i] + "님 "
        }
    }
    return people + ment
}

print(makeMessage(name: "Hatchling", "해츨링", "woo")) // Hatchling님, 해츨링님, woo님 안녕하세요!

In-Out 매개변수

함수의 매개변수는 기본적으로 상수입니다. 그래서 함수 내에서 매개변수를 변경하려고 하면 컴파일 에러가 발생하죠. 이유는 함수 내에서 실수로 매개변수를 변경하는 상황을 방지하기 위함입니다. 하지만 함수를 통해 원본 값을 변경하고 싶은 상황이 있을 수 있겠죠?? 그런 상황에 inout 키워드를 사용하면 해당 매개변수는 함수 내에서 변경이 가능하며 변경된 값이 원본에 덮어쓰게 됩니다.

그렇다면 inout 키워드에 상수를 넣고 함수에서 변경하면 어떻게 되냐!! inout에는 상수는 넣을 수 없습니다. 너무나 당연하겠죠?? 상수는 어떠한 상황에도 변할 수 없으니까요! inout 키워드 매개변수에는 변수만 전달할 수 있습니다!

inout 키워드 매개변수에 변경 될 원본 변수를 넣기 위해서는 변수 이름 앞에 ampersand(&)를 붙이면 됩니다.

func addAge(age: inout Int) {
    age += 1
}

var age = 20
addAge(age: &age)
print(age) // 21

함수 타입

모든 함수에는 매개변수 타입과 반환 타입으로 구성 된 함수 타입을 가지고 있습니다.

아래 예제를 참고해주세요. (함수 // 함수 타입)

func addAge(age: Int) -> Int { // (Int) -> Int
    let new_age = age + 1
    return new_age
}

func greet() -> String { // () -> String
    let message = "안녕하세요!"
}

func printMessage(name: String, ment: String) { // (String, String) -> Void
    print(name + ": " + ment)
}

func calculatePlusAndMinus(first: Int, second: Int) -> (plus: Int, minus: Int) { // (Int, Int) -> (plus: Int, minus: Int)
	let plus = first + second
	let minus = first - second
	return (plus, minus)
}

함수 타입 사용

함수 타입은 다른 타입들과 똑같이 사용할 수 있습니다. 예를 들면 Int 타입의 변수가 있는 것처럼 함수 타입의 변수가 있을 수 있습니다.

아래처럼 타입만 같으면 다양한 함수를 할당할 수 있습니다.

func addAge(age: Int) -> Int {
    age + 1
}

func addCount(number: Int) -> Int {
    number + 1
}

var addNumber: (Int) -> Int = addAge
print(addNumber(5)) // 6
addNumber = addCount
print(addNumber(10)) // 11

함수 타입을 매개변수로 전달하기

함수의 매개변수에는 다른 함수의 타입을 전달할 수 있습니다.

func makeMessage(name: String) -> String {
    return name + "님의 나이는"
}

func addAge(message: (String) -> String, age: Int) -> String {
    return message("Hatchling") + " \(age)세 입니다."
}

print(addAge(message: makeMessage(name:), age: 20)) // Hatchling님의 나이는 20세 입니다.

중첩 함수

기본적으로 함수는 전역(global) 함수이다. 하지만 중첩 함수를 통해 함수 내에서만 호출할 수 있는 함수를 만들 수 있다.

아래처럼 함수 내에 함수를 작성하여 함수 밖에서는 호출 할 수 없지만 특정 조건을 통해 활용할 수 있습니다.

func addNumber(number: Int, something: String) -> String {
	func addAge(age: Int) -> String { return "나이가 \(age + 1)세가 되었습니다." }
	func addCount(number: Int) -> String { return "횟수가 \(number + 1)이(가) 되었습니다." }
	if something == "나이" {
		return addAge(age: number)
	} else if something == "횟수" {
		return addCount(number: number)
	} else {
		return "올바르지 않은 someThing"
	}
}

print(addNumber(number: 20, something: "나이")) // 나이가 21세가 되었습니다.
print(addNumber(number: 10, something: "횟수")) // 횟수가 11이(가) 되었습니다.
print(addNumber(number: 1, something: "")) // 올바르지 않은 someThing

 

여기까지 기본적인 함수 사용법들을 정리해봤습니다. 한 번에 모든 내용을 작성하는 것이 읽을 때 편한지 아닌지 고민을 해볼 필요가 있겠네요😂