함수 Swift


함수

Functions are self-contained chunks of code that perform a specific task. You give a function a name that identifies what it does, and this name is used to “call” the function to perform its task when needed.

Swift’s unified function syntax is flexible enough to express anything from a simple C-style function with no parameter names to a complex Objective-C-style method with names and argument labels for each parameter. Parameters can provide default values to simplify function calls and can be passed as in-out parameters, which modify a passed variable once the function has completed its execution.

스위프트의 모든 함수는 형식을 가지며 함수의 파라미터와 반환 형식으로 구성된다. 스위프트의 다른형식처럼 이 형식을 사용할 수 있는데, 다른 함수로의 파라미터로 함수를 전달하고 함수로부터 반환함수를 받는다. 함수는 다른 함수내에 쓰여질 수 있으며 네스트 함수 범위에서 캡슐화 가능하다.


함수 정의 및 호출

When you define a function, you can optionally define one or more named, typed values that the function takes as input, known as parameters. You can also optionally define a type of value that the function will pass back as output when it is done, known as its return type.

Every function has a function name, which describes the task that the function performs. To use a function, you “call” that function with its name and pass it input values (known as arguments) that match the types of the function’s parameters. A function’s arguments must always be provided in the same order as the function’s parameter list.

아래 예시내의 함수는 greet(person:) 으로 불리며 사람의 이름을 입력으로받아 그 사람에 대한 인사를 반환한다. 이를 수행하기 위해 person이라 불리는 문자열 값을 받고 문자열 형식을 반환하며 이는 그 사람에 대한 인사를 포함한다.

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

이 정보의 모든 것은 함수 정의로 나타내어지고, func 키워드로 프리픽스된다. 함수의 반환 형식은 -> 으로 나타내며 반환하는 형식의 이름으로 이어진다.

The definition describes what the function does, what it expects to receive, and what it returns when it is done. The definition makes it easy for the function to be called unambiguously from elsewhere in your code:

print(greet(person: "Anna"))
// Prints "Hello, Anna!"
print(greet(person: "Brian"))
// Prints "Hello, Brian!"
You call the greet(person:) function by passing it a String value after the person argument label, such as greet(person: "Anna"). Because the function returns a String value, greet(person:) can be wrapped in a call to the print(_:separator:terminator:) function to print that string and see its return value, as shown above.

NOTE

The print(_:separator:terminator:) function doesn’t have a label for its first argument, and its other arguments are optional because they have a default value. These variations on function syntax are discussed below in Function Argument Labels and Parameter Names and Default Parameter Values.

The body of the greet(person:) function starts by defining a new String constant called greeting and setting it to a simple greeting message. This greeting is then passed back out of the function using the return keyword. In the line of code that says return greeting, the function finishes its execution and returns the current value of greeting.

You can call the greet(person:) function multiple times with different input values. The example above shows what happens if it is called with an input value of "Anna", and an input value of "Brian". The function returns a tailored greeting in each case.

To make the body of this function shorter, you can combine the message creation and the return statement into one line:

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

함수 파라미터와 반환 값들

함수 파라미터들과 반환 값들은 스위프트에서 완전히 유연하다. 단일 이름없는 파라미터를 갖는 간단 유틸리티 함수로 부터 다른 파라미터 옵션을 가진 표현적 파라미터 이름을 가진 복잡한 함수까지 어떤 것이든 정의 할 수 있다.

파라미터가 없는 함수들

Functions are not required to define input parameters. Here’s a function with no input parameters, which always returns the same String message whenever it is called:

func sayHelloWorld() -> String {
    return "hello, world"
}
print(sayHelloWorld())
// Prints "hello, world"
The function definition still needs parentheses after the function’s name, even though it does not take any parameters. The function name is also followed by an empty pair of parentheses when the function is called.

다중 파라미터가 있는 함수들

Functions can have multiple input parameters, which are written within the function’s parentheses, separated by commas.

This function takes a person’s name and whether they have already been greeted as input, and returns an appropriate greeting for that person:

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!"
You call the greet(person:alreadyGreeted:) function by passing it both a String argument value labeled person and a Bool argument value labeled alreadyGreeted in parentheses, separated by commas. Note that this function is distinct from the greet(person:) function shown in an earlier section. Although both functions have names that begin with greet, the greet(person:alreadyGreeted:) function takes two arguments but the greet(person:) function takes only one.

반환값이 없는 함수들

Functions are not required to define a return type. Here’s a version of the greet(person:) function, which prints its own String value rather than returning it:

func greet(person: String) {
    print("Hello, (person)!")
}
greet(person: "Dave")
// Prints "Hello, Dave!"
Because it does not need to return a value, the function’s definition does not include the return arrow (->) or a return type.

NOTE

Strictly speaking, this version of the greet(person:) function does still return a value, even though no return value is defined. Functions without a defined return type return a special value of type Void. This is simply an empty tuple, which is written as ().

The return value of a function can be ignored when it is called:

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 does not return a value
The first function, printAndCount(string:), prints a string, and then returns its character count as an Int. The second function, printWithoutCounting(string:), calls the first function, but ignores its return value. When the second function is called, the message is still printed by the first function, but the returned value is not used.

NOTE

Return values can be ignored, but a function that says it will return a value must always do so. A function with a defined return type cannot allow control to fall out of the bottom of the function without returning a value, and attempting to do so will result in a compile-time error.

다중 반환값이 있는 함수들

함수에 반환값으로 튜플 형식을 사용할수 있는데 하나의 복합 반환 값으로 여러개의 값을 가지기 위한 것이다.

아래 예시는 minMax(array:) 로서 최소와 최대값을 얻는다.

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

The minMax(array:) function returns a tuple containing two Int values. These values are labeled min and max so that they can be accessed by name when querying the function’s return value.

The body of the minMax(array:) function starts by setting two working variables called currentMin and currentMax to the value of the first integer in the array. The function then iterates over the remaining values in the array and checks each value to see if it is smaller or larger than the values of currentMin and currentMax respectively. Finally, the overall minimum and maximum values are returned as a tuple of two Int values.

Because the tuple’s member values are named as part of the function’s return type, they can be accessed with dot syntax to retrieve the minimum and maximum found values:

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"
Note that the tuple’s members do not need to be named at the point that the tuple is returned from the function, because their names are already specified as part of the function’s return type.

옵셔널 튜플 반환 형식

만약 함수로 부터 반환된 튜플 형식이 전체 튜플에 대한 "값없음"이면 옵셔널 튜플을 사용해 이를 반영하고 전체 튜플을 닐로 할 수 있다. 튜플 형식의 닫기 괄호 뒤에 물음표를 배치함으로서 옵셔널 튜플을 작성할 수 있는데 (Int, Int)? 또는 (String, Int, Bool)? 과 같은 형식이 될 수 있다.

일러두기)
(Int, Int)? 와 같은 옵셔널 튜플 형식은 (Int?, Int?)형식의 튜플과는 다르다. 옵셔널 튜플로 전체 튜플이 옵셔널이 된다.

위의 minMax(array:) 함수는 두 Int값을 가지는 튜플을 반환한다. 그러나 함수는 건내어진 배열에 안전체크를 하지 않고 있다. 만약 배열 매개변수가 빈 배열이라면 array[0] 에 접근할 때 런타임 에러가 발생할 수 있다.

빈 배열안전을 다루기 위해 minMax(array:) 함수를 옵셔널 튜플 반환형식으로 작성하고 배열이 빈 경우 닐을 반환한다.

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

You can use optional binding to check whether this version of the minMax(array:) function returns an actual tuple value or 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"

함수 매개변수 레이블과 파라미터 이름

각 함수 파라미터는 매개변수 레이블과 파라미터 이름을 갖는다. 매개변수 레이블은 함수를 호출할 때 쓰여지고, 각 매개변수는 앞에 매개변수 레이블과 함께 작성된다. 함수의 구현에서 쓰이는 파라미터 이름이 쓰인다. 기본적으로, 파라미터들은 매개변수 레이블로 파라미터 이름을 사용한다.

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)
All parameters must have unique names. Although it’s possible for multiple parameters to have the same argument label, unique argument labels help make your code more readable.

매개변수 레이블 지정하기

매개변수 레이블을 파라미터 이름 앞에 쓰고 공백으로 분리한다.
func someFunction(argumentLabel parameterName: Int) {
  // In the function body, parameterName refers to the argument value for that parameter.
}

사람의 이름과 홈타운을 받고 인사를 반환하는 greet(person:) 함수의 베리에이션을 보여준다.

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

The use of argument labels can allow a function to be called in an expressive, sentence-like manner, while still providing a function body that is readable and clear in intent.

매개변수 레이블 생략하기

If you don’t want an argument label for a parameter, write an underscore (_) instead of an explicit argument label for that parameter.

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(1, secondParameterName: 2)
If a parameter has an argument label, the argument must be labeled when you call the function.

기본 파라미터 값들

You can define a default value for any parameter in a function by assigning a value to the parameter after that parameter’s type. If a default value is defined, you can omit that parameter when calling the function.

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) // parameterWithDefault is 12

Place parameters that don’t have default values at the beginning of a function’s parameter list, before the parameters that have default values. Parameters that don’t have default values are usually more important to the function’s meaning—writing them first makes it easier to recognize that the same function is being called, regardless of whether any default parameters are omitted.

가변 파라미터

가변 파라미터는 특정 형식의 0개 이상의 값을 받는다. 함수가 호출되었을 때 여러 입력값을 건내어질 수 있음을 나타낼 수 있다. 파라미터의 형식 이름뒤에 ...를 넣음으로서 가변 매개변수를 지정할 수 있다.

가변 파라미턴에 값을 건내어진 값들은 함수 바디내에서 적절한 형식의 배열로 접근가능해 진다. For example, a variadic parameter with a name of numbers and a type of Double... is made available within the function’s body as a constant array called numbers of type [Double].

The example below calculates the arithmetic mean (also known as the average) for a list of numbers of any length:

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

NOTE

A function may have at most one variadic parameter.

In-Out 파라미터

함수 파라미터는 기본적으로 상수이다. 컴파일타임 에러내에서 함수의 결과의 바디내로부터 함수 파라미터의 값을 변경한다. 이 뜻은 실수에 의해 파라미터의 값을 변경할 수 없다는 것을 의미한다. 만약 파라미터의 값을 변경하는 함수를 원하고 함수가 호출되어 끝난 이후에도 변경된 값이 유지되기를 원한다면 In-out 파라미터를 대신에 지정한다.

파라미터의 형식 바로 앞에 inout 키워드를 넣음으로서 인아웃 파라미터를 작성할 수 있다. An in-out parameter has a value that is passed in to the function, is modified by the function, and is passed back out of the function to replace the original value. For a detailed discussion of the behavior of in-out parameters and associated compiler optimizations, see In-Out Parameters.

You can only pass a variable as the argument for an in-out parameter. You cannot pass a constant or a literal value as the argument, because constants and literals cannot be modified. 인아웃 파라미터로 매개변수를 전달할 때 앱퍼샌트(&) 를 변수의 이름앞에 넣음으로서 함수에 의해 값이 변경가능함을 나타낸다.

일러두기)
인아웃 파라미터는 기본 값을 가지지 않고, 가변 매개변수는 인아웃이 될 수 없다.

여기에 swapTwoInts(_:_:) 의 예시를 보여주는데 이 함수는 두개의 인아웃 파라미터를 가진다.

func swapTwoInts(_ a : inout Int, _ b:inout Int) {
  let t = a
  a = b
  b = t
}

The swapTwoInts(_:_:) function simply swaps the value of b into a, and the value of a into b. The function performs this swap by storing the value of a in a temporary constant called temporaryA, assigning the value of b to a, and then assigning temporaryA to b.

You can call the swapTwoInts(_:_:) function with two variables of type Int to swap their values. Note that the names of someInt and anotherInt are prefixed with an ampersand when they are passed to the swapTwoInts(_:_:) function:

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"

The example above shows that the original values of someInt and anotherInt are modified by the swapTwoInts(_:_:) function, even though they were originally defined outside of the function.

NOTE

In-out parameters are not the same as returning a value from a function. The swapTwoInts example above does not define a return type or return a value, but it still modifies the values of someInt and anotherInt. In-out parameters are an alternative way for a function to have an effect outside of the scope of its function body.

함수 형식들

모든 함수는 특정 함수 형식을 가지는데, 파리미터 형식과 반환형식으로 만들어진다.

예를 들어:

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

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

This example defines two simple mathematical functions called addTwoInts and multiplyTwoInts. These functions each take two Int values, and return an Int value, which is the result of performing an appropriate mathematical operation.

The type of both of these functions is (Int, Int) -> Int. This can be read as:

“A function that has two parameters, both of type Int, and that returns a value of type Int.”

Here’s another example, for a function with no parameters or return value:

func printHelloWorld() {
    print("hello, world")
}
The type of this function is () -> Void, or “a function that has no parameters, and returns Void.”

함수 형식 사용하기

스위프트에서 함수형식은 다른 형식을 사용하는 것처럼 사용할 수 있다. 예를 들어, 함수형식으로 상수나 변수를 사용할 수 있고 적절한 함수를 변수에 할당할 수 있다.

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

이 것은 다음과 같이 읽을 수 있다.

"mathFunction 이라 불리는 변수를 정의하며 두 Int 값을 취하고 Int 값을 반환한느 함수를 가진다. 이 새로운 변수는 addTwoInts 라는 함수를 참조한다."

The addTwoInts(_:_:) function has the same type as the mathFunction variable, and so this assignment is allowed by Swift’s type-checker.

You can now call the assigned function with the name mathFunction:

print("Result: \(mathFunction(2, 3))")
// Prints "Result: 5"
A different function with the same matching type can be assigned to the same variable, in the same way as for nonfunction types:

mathFunction = multiplyTwoInts
print("Result: \(mathFunction(2, 3))")
// Prints "Result: 6"
As with any other type, you can leave it to Swift to infer the function type when you assign a function to a constant or variable:

let anotherMathFunction = addTwoInts
// anotherMathFunction is inferred to be of type (Int, Int) -> Int

파라미터 형식으로의 함수 형식

(Int, Int) -> Int 와 같은 함수 형식을 다른 함수의 매개변수 형식으로 사용할 수 있다. 함수의 호출자를 위한 함수의 구현의 몇몇 측면을 함수가 호출될 때에 제공되게 할 수 있다.

여기에 위로 부터 math 함수의 결과를 프린트하는 예시를 보여준다.

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

이 예시는 호출된 printMathResult(_:_:_:), 를 정의하는 것으로 3개의 파라미터가 있다. The first parameter is called mathFunction, and is of type (Int, Int) -> Int. You can pass any function of that type as the argument for this first parameter. The second and third parameters are called a and b, and are both of type Int. These are used as the two input values for the provided math function.

When printMathResult(_:_:_:) is called, it is passed the addTwoInts(_:_:) function, and the integer values 3 and 5. It calls the provided function with the values 3 and 5, and prints the result of 8.

The role of printMathResult(_:_:_:) is to print the result of a call to a math function of an appropriate type. It doesn’t matter what that function’s implementation actually does—it matters only that the function is of the correct type. This enables printMathResult(_:_:_:) to hand off some of its functionality to the caller of the function in a type-safe way.

결과 형식으로서의 함수 형식

You can use a function type as the return type of another function. You do this by writing a complete function type immediately after the return arrow (->) of the returning function.

The next example defines two simple functions called stepForward(_:) and stepBackward(_:). The stepForward(_:) function returns a value one more than its input value, and the stepBackward(_:) function returns a value one less than its input value. Both functions have a type of (Int) -> Int:

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

여기에 chooseStepFunction(backward:) 라는 함수가 있다. 결과형식은 (Int) -> Int 이다. chooseStepFunction(backward:) 함수는 stepForward(_:)함수나 stepBackward(_:)함수를 backward라는 불린값에 기반해 반환한다.

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

You can now use chooseStepFunction(backward:) to obtain a function that will step in one direction or the other:

var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the stepBackward() function
The example above determines whether a positive or negative step is needed to move a variable called currentValue progressively closer to zero. currentValue has an initial value of 3, which means that currentValue > 0 returns true, causing chooseStepFunction(backward:) to return the stepBackward(_:) function. A reference to the returned function is stored in a constant called moveNearerToZero.

Now that moveNearerToZero refers to the correct function, it can be used to count to zero:

print("Counting to zero:")
// Counting to zero:
while currentValue != 0 {
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// 3...
// 2...
// 1...
// zero!

네스트된 함수

All of the functions you have encountered so far in this chapter have been examples of global functions, which are defined at a global scope. You can also define functions inside the bodies of other functions, known as nested functions.

Nested functions are hidden from the outside world by default, but can still be called and used by their enclosing function. An enclosing function can also return one of its nested functions to allow the nested function to be used in another scope.

You can rewrite the chooseStepFunction(backward:) example above to use and return 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 now refers to the nested stepForward() function
while currentValue != 0 {
  print("\(currentValue)...")
  currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!



덧글

댓글 입력 영역