GGURUPiOS

Swift 공식 문서 정리 - (6) Functions 본문

Swift/공식문서 정리 ( 문법 )

Swift 공식 문서 정리 - (6) Functions

꾸럽 2023. 4. 19. 16:19

Functions ( 함수 )

함수는 특정 작업을 수행하는 독립적인 코드 덩어리임.

다양한 함수 유형이 있음

함수 정의 및 호출

함수를 정의할 때 func 키워드와 함수명 파라미터 형, 반환 형을 정의 함

func greet(person: String) -> String {
    let greeting = "Hello, " + person + "!"
    return greeting
}

// 더 짧게 만들기 

func greetAgain(person: String) -> String {
    return "Hello again, " + person + "!"
}

함수 파라미터(매개변수)와 반환 값

스위프트에서 파라미터와 반환값은 매우 유연함. 여러 유형이 있음

파라미터가 없는 함수

func sayHello() -> String {
	return "hello, wolrd"
}
print(sayHello())
// "hello, world" 출력

파라미터가 여러개인 함수

쉼표로 구분해서 여러 파라미터를 받을 수 있음

func greet(person: String, alreadyGreeted: Bool) -> String {
    if alreadyGreeted {
        return greetAgain(person: person)
    } else {
        return greet(person: person)
    }
}
print(greet(person: "Tim", alreadyGreeted: true))
// Prints "Hello again, Tim!"

반환 값이 없는 함수

반환 값이 없을 경우엔 → 를 안 써도 됨

하지만 엄밀히 말하면 반환값이 없는 것은 아니고 Void라는 특수 값을 반환함 ( 생략 하는 느낌 )

Void는 빈 튜플임

func greet(person: String) {
    print("Hello, \\(person)!")
}
greet(person: "Dave")
// Prints "Hello, Dave!"

함수자체에 반환값이 있지만, 함수의 반환값은 호출 될 때 무시할 수 있음

func printAndCount(string: String) -> Int {
    print(string)
    return string.count
}
func printWithoutCounting(string: String) {
    let _ = printAndCount(string: string)
}
printAndCount(string: "hello, world")
// prints "hello, world" and returns a value of 12
printWithoutCounting(string: "hello, world")
// prints "hello, world" but doesn't return a value

반환 값이 여러개인 함수

여러값을 반환하는 함수의 반환 유형으로 튜플 유형을 사용할 수 있음

func minMax(array: [Int]) -> (min: Int, max: Int) {
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}

// 사용하는 곳에서 접근을 하려면 ?
let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("min is \\(bounds.min) and max is \\(bounds.max)")

// 튜플 멤버의 값은 반환 유형의 일부로 이름이 지정되기 때문에, 닷 구문 으로 접근하여
// 위와같이 접근이 가능 함

옵셔널 튜플 반환 유형

전체 튜플에 대해 값이 없을 가능성이 있는 경우 옵셔널 튜플 반환 유형을 사용하여 그 사실을 반영할 수 있음

(Int, Int)? 처럼 사용하면 됨

func minMax(array: [Int]) -> (min: Int, max: Int)? {
    if array.isEmpty { return nil }
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}

// 주의할 점은 (Int, Int)? 와 (Int?, Int?) 는 다른 것임. 
// 전자는 전체튜플에 대한 옵셔널이고, 후자는 각 개별 값에 대한 옵셔널임

암시적 반환이 있는 함수

함수의 전체 본문이 단일 식인 경우 함수는 암시적으로 해당 식을 반환 함 ( 그냥 한줄일 때는 return 을 생략 할 수 있다고 생각하면 될 듯 함 )

func greeting(for person: String) -> String {
    "Hello, " + person + "!"
}
print(greeting(for: "Dave"))
// Prints "Hello, Dave!"

func anotherGreeting(for person: String) -> String {
    return "Hello, " + person + "!"
}
print(anotherGreeting(for: "Dave"))
// Prints "Hello, Dave!"

함수 인자 라벨과 파라미터 이름

적절한 파라미터 이름을 지정해 함수 내부와 호출 시 사용할 수 있음

모든 매개변수는 고유한 이름을 가져야 함.

func someFunction(firstParameterName: Int, secondParameterName: Int) {
    // 함수 내부에서 firstParameterName와 secondParameterName의 인자를 사용합니다.
}
someFunction(firstParameterName: 1, secondParameterName: 2)

인자 라벨 지정

파라미터 이름 앞에 공백으로 구분하여 인수 레이블을 작성함

함수내부에서 식별자와, 호출시 사용하는 이름을 다르게 해서 사용할 수 있다는 말

읽기 쉽고 의도가 명확한 함수 본문을 제공할 수 있다. 라고한

func someFunction(argumentLabel parameterName: Int) {
    // In the function body, parameterName refers to the argument value
    // for that parameter.
}

// 
func greet(person: String, from hometown: String) -> String {
    return "Hello \\(person)!  Glad you could visit from \\(hometown)."
}
print(greet(person: "Bill", from: "Cupertino"))
// Prints "Hello Bill!  Glad you could visit from Cupertino."

// greet 함수에서 인자 라벨을 from 으로 작성하였음
// 즉 호출 할 때는 from: 으로 값을 채워넣고, 함수 내부에서는 hometown으로 사용

인자 생략

인자 레이블을 원하지 않는 경우 밑줄(_) 을 써서 인자 값을 생략할 수 있음

func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
    // 함수 안에서 firstParameterName, secondParameterName
    // 인자로 입력받은 첫번째, 두번째 값을 참조합니다.
}
someFunction(1, secondParameterName: 2)

// someFunction(firstParameteName:1, secondParameterName: 2) 에서 첫번째 인자를 생략해서 값만 넣을 수 있게 사용 가능

기본 파라미터 값

파라미터에 대한 기본값을 정의 할 수 있음

정의 되어 있으면 함수를 호출할 때 해당 파라미터를 생략할 수 있음

func a(parameter: Int = 6) { } 이런식으로 작성 가능

func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
    // If you omit the second argument when calling this function, then
    // the value of parameterWithDefault is 12 inside the function body.
}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault is 6
someFunction(parameterWithoutDefault: 4)

/* 공식 문서에서 
기본값이 없는 파라미터는 시작부분, 있기본 값이 있는 파라미터 앞에 배치 함.
기본값이 없는 파라미터가 일반적으로 중요 함.
*/

가변(집합) 파라미터 값

가변 파라미터는 지정된 유형의 값을 0개 이상 허용 함.

… 을 이용해서 사용 가능함

func arithmeticMean(_ numbers: Double...) -> Double {
    var total: Double = 0
    for number in numbers {
        total += number
    }
    return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers

한가지 이해가 안되는? 궁금한 점은 numbers: [Double]로 받으면 안될까..? 라는 의문이 생김

chatCPT 에게 물어봤다.

단순 호출 방식의 차이라고 한다. 배열자체를 받으면

arithmeticMean([1, 2, 3, 4, 5])로 호출해야 하고, 가변 파라미터로 인자를 받으면

arithmeticMean(1,2,3,4,5) 이런식으로 호출

그래서 또 다른 차이가 없을까 하고 찾아봤다. 그렇지만 거의 성능 차이는 거의 없다고 한다.

가변 파라미터로 받을 시에 함수 내부적으로 배열을 생성해서 처리하기 때문이라고 함.

대규모의 데이터가 아닌이상 거의 차이는 없다고 한다. 대규모의 데이터 일 때도 배열로 받는것이 아주 미세하게 빠를 수 있다고 함.

가변 파라미터는 아예 써본적이 없어서 궁금했다. 알고나서도 거의 쓸 일이 없어보임..

in-out 파라미터

함수 매개변수는 기본적으로 상수임

in-out 파라미터는 인자값을 직접 변경하는 파라미터 임

즉 리턴값을 사용하지 않고 함수 외부의 값을 변경할 수 있는 하나의 방법임.

따라서 인자로 오는 두 값은 변수로만 전달 되어야 함. 상수와 리터럴은 수정할 수 없기 때문에 전달 불가능함

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}
// 두 파라미터의 값이 서로 바뀌는 함수

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \\(someInt), and anotherInt is now \\(anotherInt)")

// 함수 밖에 선언된 변수 someInt, anotherInt 의 값이 바뀐 모습. 

함수 유형 ( 함수 타입 )

모든 함수에는 파라미터 유형과 반환 유형으로 구성된 특정 함수 유형이 있음

func addTwoInts(_ a: Int, _ b: Int) -> Int {
    return a + b
}
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
    return a * b
}

// 두 함수의 유형은 (Int, Int) -> Int 와 같음

func printHelloWorld() {
    print("hello, world")
}

// 위 함수의 유형은 () -> Void 임

함수 유형 사용

상수 또는 변수를 함수유형으로 정의하고 해당 변수에 함수를 할당할 수 있음

말이 어려운데 간단히 생각해보면

var a: Int = 5처럼 타입선언 하고 값을 저장 하듯이

함수도 똑같이 변수나 상수에 할당 할 수 있다는 뜻

var mathFunction: (Int, Int) -> Int = addTwoInts

→ 파라미터로 Int, Int 형을 받고 Int를 방출하는 타입을 선언, mathFunction이라는 변수에 addTwoInts라는 함수를 할당 함.

타입 추론으로 타입선언역시 생략 가능

let anotherMathFunction = addTwoInts
// antoherMathFunction의 타입은 (Int, Int) -> Int 가 되는 것임

파라미터 유형으로서의 함수 유형

간단하게는 파라미터에 함수유형도 받을 수 있다.. 정도로 이해

func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
    print("Result: \\(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)

반환 유형으로서의 함수 유형

함수를 반환하는 함수를 만들 수 있음

func stepForward(_ input: Int) -> Int {
    return input + 1
}
func stepBackward(_ input: Int) -> Int {
    return input - 1
}

func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    return backward ? stepBackward : stepForward
}

// 즉, 반환하는 타입을 (Int) -> Int 함수유형으로 선언해서 사용할 수 있다.

중첩 함수

모든 함수는 전역 범위에서 정의된 전역 함수의 예였음

중첩함수는 다른함수 본문 내부에 함수를 정의 할 수 있음.

함수 내부에서만 사용 되기 때문에 가독성과 모듈성을 높이는 데 좋을 듯 함.

func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    func stepForward(input: Int) -> Int { return input + 1 }
    func stepBackward(input: Int) -> Int { return input - 1 }
    return backward ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
    print("\\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!