Swift study - Functions

rbw·2022년 3월 6일
0

swift-study

목록 보기
10/17
post-thumbnail

Swift - Functions

함수는 특정 작업을 수행하는 자체 포함된 코드의 덩어리이다. 함수에 이름을 지정하고, 이 이름은 작업이 필요할 때 수행을 하기위해 함수를 호출 할때 사용한다.

스위프트의 통일된 함수 구문은 매개변수 이름이 없는 C언어 스타일의 함수로 부터 각 매개변수에 이름과 인수 라벨링된 복잡한 Objective-C 스타일의 메소드 까지 모든 것을 표현할 수 있을만큼 유연하다. 매개변수는 함수 호출을 간단히 하기 위해 기본 값을 제공할 수 있고, 함수 실행이 완료되면 전달된 변수를 수정하는 in-out 파라미터로 전달이 가능하다.

스위프트의 모든 함수는 매개변수 타입과 반환 타입으로 이루어진 타입을 가지고 있다. 스위프트의 다른 타입 전부에서 사용이 가능하고, 이는 다른 함수로 매개변수를 전달하는걸 쉽게 만들어주고, 함수로 부터 함수를 반환하게 해준다. 함수는 중첩 함수 스코프에서 유용한 기능을 캡슐화하기 위해 다른 함수의 내부에서 작성할 수 있다.

Defining and Calling Functions (함수의 정의와 호출)

함수를 정의할 때, 선택적으로 매개변수라고 하는 입력으로 사용하는 하나 이상의 유형이 지정된 값을 정의 할 수 있다. 또 선택적으로 함수가 종료될 때 반환타입이라고 알려진 출력으로 전달할 때 사용하는 값의 타입을 정의할 수 있다.

parameter(매개변수) : 함수의 정의에 포함되는 변수

argument(전달인자) : 함수를 호출 할 때 전달하는 실제 값

모든 함수는 함수가 수행하는 작업을 나타내는 이름을 가지고 있다. 함수를 사용하기 위해 함수의 이름과 매개변수에 타입에 맞는 값을 일치하게 하여서 호출 한다. 함수의 인자는 반드시 호출할 함수의 매개변수 목록의 순서에 맞게 제공해야한다.

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

func 키워드와 함께 함수의 정의 안에서 함수의 모든 정보는 작성된다. 함수의 선언부에서 리턴타입의 타입을 -> 라는 반환 화살표를 사용해서 명시할 수 있다. 이는 반환하는 타입의 이름을 따른다.

함수가 무엇을 하는지, 어떤 것을 받는지, 종료시 반환하는 것에 대해 정의는 설명한다. 정의는 코드의 다른 곳에서도 모호하지 않게 함수를 호출하는 것을 쉽게 만들어 준다.

print(greet(person: "Anna"))
// Prints "Hello, Anna!"
print(greet(person: "Brian"))
// Prints "Hello, Brian!"

NOTE

print(_:separator:terminator:) 함수는 처음 인자에서 라벨을 가지고 있지 않다. 그리고 다른 인자는 선택적으로 사용이 가능하다. 기본 값을 가지고 있기 때문이다.

위 함수를 좀더 짧게 작성하려면 다음과 같다.

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

Function Parameters and Return Values (함수의 매개변수와 반환 값)

스위프트에서 함수의 매개변수와 반환 값은 매우 유연하다. 매개변수의 이름 없는 간단한 함수로부터 매개변수의 이름과 다른 매개변수의 옵션을 포함하는 복잡한 함수까지 모든 것을 정의할 수 있다.

Functions Without Parameters (매개변수 없는 함수)

함수의 매개변수는 필수가 아니다. 다음 함수는 언제나 같은 값을 반환 한다.

func sayHelloWorld() -> String {
    return "hello, world"
}
print(sayHelloWorld())
// Prints "hello, world

어떤 매개변수를 가지지 않더라도 함수의 이름 옆의 소괄호는 필수이다. 호출시에도 함수의 이름 옆에 소괄호는 필수로 작성해야한다.

Functions With Multiple Parameters (여러 매개변수를 가진 함수)

함수는 또한 소괄호 안에서 콤마로 구분하여 여러 매개변수를 가질 수 있다.

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!"

앞에서 작성한 greet(person:)과 위의 greet(person:alreadyGreeted:) 함수는 매개변수의 개수가 다르므로 이름은 같지만 다른 함수이다.

Functions Without Return Values (반환 값이 없는 함수)

함수의 반환값도 필수는 아니다. 다음은 문자열 값을 반환하는 대신 출력을 하는 함수이다.

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

함수의 반환 값이 없으므로, 정의부에서 ->로 함수의 반환 타입을 명시하지 않는다.

NOTE

엄격히 얘기해서, 위의 함수는 여전히 반환 값이 존재한다. 비록 반환 값이 정의가 안되었지만. 함수는 반환타입 정의 없이 Void 라는 특별한 값을 리턴한다. 이는 간단히 () 로 작성하는 심플한 빈 튜플이다.

호출 될 때 함수의 반환 값은 무시될 수 있다.

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

printAndCount 함수는 반환을 하지만, printWithoutCounting 함수는 반환 하는 값이 없다.

NOTE

리턴값은 무시될수 있다, 그러나 반환 값이 있는 함수는 반환을 해야만 한다. 리턴타입이 있는 함수는 마지막 부분에서 리턴 값 없이 제어하는것은 허가 되지 않고, 이는 컴파일 에러를 보여준다.

Functions with Multiple Return Values (여러 반환 값이 있는 함수)

튜플을 사용하여 함수는 하나의 복합 리턴 값의 일부로 여러 값을 반환 할 수 있다.

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)
}

minMax(array:) 함수는 두개의 정수값을 포함하는 튜플을 반환한다. 이 값들은 min, max로 라벨링이 되어있고, 함수의 반환 값을 검색할 때 이름으로 접근이 가능하다.

튜플의 멤버값은 함수의 반환값의 일부로 이름이 있기 때문에, 최소값과 최대값을 검색할 때 이름으로 접근이 가능하다.

let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("min is \(bounds.min) and max is \(bounds.max)")
// Prints "min is -6 and max is 109"

이미 함수의 반환 타입의 부분으로 이름이 있기 때문에, 함수를 사용하여 반환 하는 지점에서 이름을 붙일 필요는 없다.

Optional Tuple Return Types (옵셔널 튜플 반환 타입)

전체 튜플에서 값이 없는 가능성이 있는 함수로 부터 튜플을 반환한다면, 이 사실을 반영하기위해 nil을 사용하여 리턴 타입으로 옵셔널 튜플을 사용할 수 있다. 사용 예시는 다음과 같다. (Int, Int)? or (String, Int, Bool)?

NOTE

옵셔널 튜플 타입인 (Int, Int)? 와 옵셔널 타입을 포함하는 튜플인 (Int?, Int?)는 다르다. 옵셔널 튜플 타입은 전체 튜플이 옵셔널이라는 의미이고, 튜플 안에 각각이 옵셔널이라는 의미가 아니다.

minMax(array:) 함수는 배열을 넘겨 받을 때 어떠한 안전성 체크도 하지 않는다. 만약 array가 비어 있다면, 위의 minMax(array:) 함수는 비어있는 인덱스에 접근을 하게 되므로 런타임 에러를 발생 시킬것이다.

이러한 부분을 방지하기 위해, 함수의 옵셔널 튜플 타입을 명시하고, 배열이 비어 있을 때 nil을 반환하도록 작성한다.

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)
}

// 옵셔널 바인딩으로 사용하는 모습, 함수는 nil 또는 실제 튜플 값을 반환한다.
if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
    print("min is \(bounds.min) and max is \(bounds.max)")
}
// Prints "min is -6 and max is 109"

Functions With an Implicit Return (암시적으로 반환하는 함수)

함수가 하나의 표현식이라면, 함수는 표현식에서 반환하는 것을 명시하지 않아도 된다. 밑의 두 함수는 같은 동작을 한다.

// 한 줄의 return으로 작성이 된다면 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!"

NOTE

암시적으로 반환하는 함수는 몇몇의 값을 반환해야한다. 따라서 print(13)같은 것은 암시적 반환 값으로 사용이 불가하다. 그러나 스위프트는 fatalError("Oh no!")와 같이 암시적 반환이 발생하지 않는 것을 알고 있다면,암시적 반환 값으로 사용이 가능하다.

Function Argument Labels and Parameter Names (함수 매개변수의 이름과 인자 라벨링)

각 함수의 파라미터는 인자 라벨과 매개변수 이름을 가지고 있다. 인자 라벨은 함수를 호출할 때 사용하며, 각각의 인자는 함수 호출시 인자 레이블 다음에 작성이 된다. 매개변수의 이름은 함수의 구현에서 사용된다. 기본적으로, 매개변수의 이름을 인자 라벨에 사용한다.

func someFunction(firstParameterName: Int, secondParameterName: Int) {
    // In the function body, firstParameterName and secondParameterName
    // refer to the argument values for the first and second parameters.
}
someFunction(firstParameterName: 1, secondParameterName: 2)

모든 매개변수는 반드시 중복없는 이름을 가져야한다. 같은 인자 라벨을 가지는 여러개의 매개변수가 가능할지라도, 중복없는 인자 라벨은 코드의 가독성을 높여주는데 도와준다.

Specifying Argument Labels (인자 라벨 정의)

공백으로 구분하여 매개변수 이름 전에 인자 라벨을 작성한다.

func someFunction(argumentLabel parameterName: Int) {
  // 함수 본문에서 parameterName 은 인자 값을 나타낸다. 
}

// hometown 매개변수를 인자 라벨로 from 으로 받는 모습
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."

인수 라벨을 사용하면 문장과 같은 표현방식으로 함수를 호출할 수 있고, 읽기 쉽고 의도가 명확한 함수 바디를 제공할 수 있습니다.

Omitting Argument Labels (인자 라벨의 생략)

_를 사용하여 매개변수의 인자라벨을 생략하고 함수를 호출 할 수 있다.

func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
  
}
someFunction(1, secondParameterName: 2)

매개변수가 인자라벨이 있을 때, 함수를 호출시에는 인수에 라벨을 지정 해줘야 한다.

Default Parameter Values (기본 매개변수 값)

매개변수의 타입을 지정한 후에 기본 값을 정의할 수 있다. 기본 값이 정의가 되었다면, 호출시에 매개변수를 생략이 가능하다.

func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
    // 두번째 매개변수를 생략하면 기본값 12가 할당된다.
}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault is 6
someFunction(parameterWithoutDefault: 4) // parameterWithDefault is 12

기본값이 존재하지 않는 파라미터는 보통 함수의 의미에서 더 중요하다. 처음에 기본값이 없는 파라미터를 작성하는 것은 어떠한 기본 파라미터가 생략된지 아닌지에 관련없이, 같은 함수를 호출했다는 것의 인지를 더 쉽게 해준다.

Variadic Parameters (가변 매개변수)

가변 매개변수는 0개 이상의 특정 타입 값을 허용한다. 함수가 호출시에 입력 값의 수가 변하여 전달 될 수 있는 파라미터를 특정하기 위해 가변 매개변수를 사용한다. 매개변수 타입이름에 ...을 붙여서 사용한다.

가변 매개변수에 전달된 값은 적절한 타입의 배열로 함수 본문에서 사용이 가능하게 한다. 예를 들어, 밑의 예시에서 numbers는 함수 본문에서 [Double] 타입인 상수 배열로 사용된다.

// 평균을 구하는 함수
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

In-Out Parameters (인-아웃 매개변수)

기본적으로 함수의 파라미터는 상수이다. 함수의 내부에서 값을 변경하려고 하면 컴파일 에러의 결과가 일어난다. 이는 실수로 매개변수의 값을 변경하지 못한다는 의미이다. 만약 함수의 매개변수 값을 변경하고 싶고, 함수의 호출이 끝난 후에도 유지하고 싶다면, 매개변수 대신 in-out 매개변수를 정의해라.

inout 키워드를 매개변수 타입 전에 작성하여 사용이 가능하다. 인-아웃 매개변수는 함수 내에서 전달된 값을 가지고 있고, 함수에 의해 변경이 되며, 함수 밖으로 원본 값을 대체하여 전달한다.

인-아웃 파라미터에는 오직 변수만 전달이 가능하다. 상수나 리터럴 값은 변경할 수 없으므로 전달이 불가하다. 함수를 호출시에 &를 변수 이름 전에 위치해야 사용이 가능하다.

NOTE

인-아웃 파라미터는 기본값을 가질 수 없고, 가변 매개변수는 inout 키워드를 작성 할 수 없다.

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)")
// Prints "someInt is now 107, and anotherInt is now 3"

위의 예시로 함수의 밖에서도 값이 바뀐 것을 볼 수 있다.

NOTE

이것은 함수에서 값을 반환 하는 것과는 다르다. 인-아웃 파라미터는 함수 본문 외부 스코프에 영향을 주는 하나의 방법이다.

Function Types (함수 타입)

모든 함수는 특정 타입을 가지고 있고, 매개변수 타입과 함수의 반환 타입으로 구성되었다.

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

// () -> Void 라는 의미이다. 함수는 파라미터가 없고, Void를 반환한다는 의미
func printHelloWorld() {
    print("hello, world")
}

Using Function Types (함수 타입의 사용)

스위프트의 다른 타입 처럼 함수 타입을 사용한다. 예를 들어, 상수나 변수의 타입으로 함수타입을 정의할 수 있고 변수에 적절한 함수로 할당이 가능하다.

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

이것의 의미는 다음과 같다. "mathFunction이라는 이름의 변수를 정의하고, 이는 두개의 정수 값을 얻어서 정수 값을 반환하는 함수의 타입을 가지고 있다. 그리고 이 변수를 addTwoInts라는 함수를 참조하도록 설정해라"

addTwoInts(_:_:) 함수는 위 변수랑 같은 타입으로, 스위프트 타입체커에 의해 할당이 허용된다.

// mathFunction라고 할당된 함수를 호출하는 모습
print("Result: \(mathFunction(2, 3))")
// Prints "Result: 5"

// 같은 타입을 가지는 다른 함수도 변수에 할당이 가능하다.
mathFunction = multiplyTwoInts
print("Result: \(mathFunction(2, 3))")
// Prints "Result: 6"

// 상수나 변수에 함수를 할당할 때
// 다른 타입과 같이 함수의 타입을 유추하기 위해
// 스위프트의 추론 기능을 이용할 수 있다.
let anotherMathFunction = addTwoInts
// anotherMathFunction의 타입은 (Int, Int) -> Int 로 유추된다.

Function Types as Parameter Types (매개변수 타입으로의 함수 타입)

(Int, Int) -> Int 같은 함수의 타입을 다른 함수의 매개변수 타입으로 사용이 가능하다. 이는 함수를 호출할 때 함수 호출자가 제공할 함수의 구현의 일부를 남길 수 있다.

// 사용 예시
func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
    print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// Prints "Result: 8"

이렇게 하면, printMathResult(_:_:_:)가 타입-안전한 방식으로 함수 호출자에게 일부 기능을 넘겨줄 수 있다.

Function Types as Return Types (반환 타입으로의 함수 타입)

다른 함수의 리턴 타입으로 함수 타입을 사용 가능하다. -> 뒤에 함수 유형을 작성하여서 사용이 가능하다.

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
}

var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero는 stepBackward() 함수를 참조한다.

// 사용 예시
while currentValue != 0 {
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// 3...
// 2...
// 1...
// zero!

Nested Functions (중첩된 함수)

이번 장에서 마주한 모든 함수는 전역 스코프에서 정의된 전역 함수들의 예시였다. 다른 함수의 내부에서 함수를 정의하여 중첩함수로 알려진 기능을 사용 할 수 있다.

중첩된 함수는 기본적으로 외부에서는 숨겨져있다. 그러나 중첩함수를 둘러싼 함수에서 호출되고 사용된다. 둘러싼 함수는 또한 중첩된 함수를 반환 가능하고, 이는 다른 범위에서 중첩된 함수를 사용할 수 있게 해준다.

// 위의 예시를 중첩함수로 작성
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는 중첩된 stepForward() 함수를 참조한다.

while currentValue != 0 {
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!
profile
hi there 👋

0개의 댓글