07. 함수

JinStory77·2022년 5월 13일
0

Swift 공부_기초

목록 보기
7/11

함수

  • 함수란 작업의 가장 작은 단위이자 하나의 작은 프로그램이기도 하다. 프로그램을 이루는 주된 요소 중 하나이다.
  • 스위프트에서 함수는 일급 객체이기 때문에 하나의 값으로 사용할 수 있다.
  • 스위프트의 함수는 자유도가 굉장히 높다.
    • 일급 객체란
      • 객체가 런타임에도 생성이 가능함
      • 객체를 인자 값으로 전달할 수 있음
      • 객체를 반환 값으로 사용할 수 있음
      • 데이터 구조 안에 저장할 수 있어야 함

1. 함수와 메서드

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


2. 함수의 정의와 호출

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

2.1. 기본적인 함수의 정의와 호출

  • 함수 정의 키워드는 func이다.

  • 함수의 이름, 매개변수(parameter), 반환 타입(Return Type)등으로 함수를 정의한다.

    • 매개변수(parameter) = 함수를 정의할 때 사용하는 이름
    • 전달인자(argument) = 함수를 호출할때 사용하는 이름, 매개변수의 역할을 좀 명확하게 표현하고자 할 때 사용 / ex) to, from
  • 함수 이름을 지정해준 후 매개변수는 소괄호(())로 감싸준다. '->'를 사용하여 어떤 타입이 반환(return)될 것인지 명시해준다.

  • 기본 형태의 함수 정의와 사용

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

let helloJenny: String = hello(name: "Jenny")   
// 함수 = hello(name:), 매개변수 = name, 전달인자 = Jenny
print(helloJenny)

func introduce(name: String) -> String {
    // [return "제 이름은 " + name + "입니다"]와 같은 작동을 합니다.
    "제 이름은 " + name + "입니다"
}

let introduceJenny: String = introduce(name: "Jenny")
print(introduceJenny)

2.2. 매개변수

  • 매개 변수가 여러개 필요한 함수에는 쉼표(,)로 구분한다.
  • 매개 변수 이름을 붙여주고 콜론(:)을 적어준 후 전달인지를 적는다. 이렇게 호출 시에 매개변수에 붙이는 이름을 매개변수 이름(parameter name)이라고 한다.
  • 함수에 매개변수가 필요 없다면 매개변수를 공란으로 비워둔다
func helloWorld() -> String {
   return "Hello, world!"
}

print(helloWorld())      // Hello, world!

  • 매개변수가 여러 개인 함수의 정의와 사용
func sayHello(myName: String, yourName: String) -> String {
    return "Hello \(yourName)! I'm \(myName)"
}

print(sayHello(myName: "Jin", yourName: "Jenny"))
// "Hello Jenny! I'm Jin"

  • ✓ 매개변수 이름과 전달인자 레이블을 가지는 함수 정의와 사용
    • from과 to라는 전달인자 레이블이 있다
    • 함수 내부에서는 전달인자를 사용할 수 없으며, 함수를 호출할 때는 매개변수 이름을 사용할 수 없다.
    • 기본 값이 있는 매개변수를 뒷쪽에 배치한다.
func 함수 이름(`전달인자 레이블` `매개변수1이름:` `매개변수1타입,` `전달인자 레이블` `매개변수2이름:`
`매개변수2타입`) -> `반환 타입` {
`실행 구문`
return `반환 값`
}
// from과 to라는 전달인자가 있으며
// myName과 name이라는 매개변수 이름이 있는 sayHello 함수
func sayHello(from myName:String, to name:String) -> String {
    return "Hello \(name)! I'm \(myName)"
}

print(sayHello(from: "Jin", to: "Jenny"))
// Hello Jenny! I'm Jin

  • 전달인자 레이블이 없는 함수 정의와 사용
func sayHello(_ name: String, _ times: Int) -> String {
    var result: String = ""

    for _ in 0..<times {
        result += "Hello \(name)!" + " "
    }
    return result
}

print(sayHello("Chope", 2))
//Hello Chope! Hello Chope!

  • 전달인자 레이블 변경을 통한 함수 중복 정의
func sayHello(to name: String, _ times: Int) -> String {
    var result: String = " "
    
    for _ in 0..<times {
        result += "Hello \(name)!" + " "
    }
    
    return result
}

func sayHello(to name: String, repeatCount times: Int) -> String {
    var result: String = " "
    
    for _ in 0..<times {
        result += "Hello \(name)!" + " "
    }
    return result
}

print(sayHello(to: "Chope", 2))
print(sayHello(to: "Chope", repeatCount: 2))

  • 매개변수 기본값
    • 스위프트의 함수에서는 매개변수마다 기본값을 정할 수 있다. 즉 매개변수가 전달되지 않으면 기본값을 사용한다.
    • 매개변수 기본값이 있는 함수는 중복 정의한 것처럼 사용할 수 있다.
    • 중요한 매개변수는 앞쪽으로 배치하는 것이 좋다.
// 위의 ('전달인자 레이블이 없는 함수 정의와 사용'과 비슷한 함수)
func sayHello(_ name: String, times: Int = 3) -> String {
    var result: String = ""

    for _ in 0..<times {
        result += "Hello \(name)!" + " "
    }
    return result
}
print(sayHello("Hana"))
// Hello Hana! Hello Hana! Hello Hana!
// times 매개변수의 전달 값을 넘겨주지 않아 기본값 3을 반영해서 3번 출력
print(sayHello("Joe", times: 2))
// Hello Joe! Hello Joe!
// times 매개변수의 전달 값을 2로 넘겨주었기 때문에 전달 값을 반영해서 2번 출력

  • 가변 매개변수와 입출력 매개변수
    • 매개변수로 몇 개의 값이 들어올지 모를 때 사용할 수 있다.
    • 0개 이상의 값을 받아올 수 있으며, 가변 매개변수로 들어온 인자 값은 배열처럼 사용할 수 있다.
    • 함수마다 가변 매개변수는 하나만 가질 수 있다.
func sayHelloToFriends(me: String, friends names: String...) -> String {
    var result: String = ""
    
    for friend in names {
        result += "Hello \(friend)!" + " "
    }
    result += "I'm " + me + "!"
    return result
}

print(sayHelloToFriends(me: "Jin", friends: "Johansson", "Jay", "Wizplan"))
// Hello Johansson! Hello Jay! Hello Wizplan! I'm Jin!

print(sayHelloToFriends(me: "Jin"))
// I'm Jin!

  • inout 매개변수의 활용
var numbers: [Int] = [1, 2, 3]

func nonReferenceParameter(_ arr: [Int]) {
    var copiedArr: [Int] = arr
    copiedArr[1] = 1
}

func referenceParameter(_ arr: inout [Int]) {
    arr[1] = 1
}

nonReferenceParameter(numbers)
print(numbers[1])       // 2

referenceParameter(&numbers)   // 참조를 표현하기 위해 & 붙여줌
print(numbers[1])       // 1

2.3. 반환이 없는 함수

  • 반환 값이 없는 함수라면 반환 타입을 '없음'을 의미하는 'Void'로 표기하거나 아예 반환 타입을 생략해도 된다.
func sayHelloWorld() {
    print("Hello, world!")
}
sayHelloWorld()    // Hello, world!

func sayHello(from myName: String, to name: String) {
    print("Hello \(name)! I'm \(myName)")
}
sayHello(from: "Jin", to: "Mijeong")    // Hello Mijeong! I am Jin

func sayGoodbye() -> Void {  // Void를 명시해주어도 상관없음
    print("Good bye")
}
sayGoodbye()   // Good bye

2.4. 데이터 타입으로서의 함수

  • 스위프르의 함수는 일급객체이므로 하나의 데이터 타입으로 사용할 수 있다.
    • 각 함수는 매개변수 타입과 반환 타입으로 구성된 하나의 타입으로 사용 (정의)할 수 있다는 뜻이다.
(매개변수 타입의 나열) -> 반환 타입

  • 함수의 축약 표현
// 동일한 표현
sayHello(name: String, times: Int) -> String
sayHello(name:times:)   

  • 함수 타입의 사용
typealias CalculateTwoInts = (Int, Int) -> Int

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

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

var mathFunction: CalculateTwoInts = addTwoInts

// var mathFunction: (Int, Int) -> Int = addTwoInts
print(mathFunction(2,5))    // 2 + 5 = 7

mathFunction = multiplyTwoInts
print(mathFunction(2,5))    // 2 * 5 = 10

  • 전달인자로 함수를 전달받는 함수
func printMathResult(_ mathFunction: CalculateTwoInts, _ a: Int, _ b: Int) {
print("Result: \(mathFunction(a, b))")
}

printMathResult(addTwoInts, 3, 5)
// Result: 8


3. 중첩함수

  • 스위프트는 데이터 타입의 중첩이 자유롭다.
    • 열거형 안에 또 하나의 열거형이 들어 갈 수 있고, 클래스 안에 또 다른 클래스가 들어올 수 잇다는 뜻, 패턴을 자유롭게 만들 수 있다.
  • 상위 함수의 몸통 블록 내부에서만 사용할 수 있다.
    • 함수가 하나의 반환 값으로 사용될 수 있으므로 중첩 함수를 담은 함수가 반환하면 밖에서도 사용할 수 있다.
// -3 | -2 | -1 | 0 | 1 | 2 | 3 | .... 이런 게임판이 있다는 가정
// 원점으로 이동하기 위한 함수
typealias MoveFunc = (Int) -> Int

func goRight(_ currentPosition: Int) -> Int {
    return currentPosition + 1
}

func goLeft(_ currentPosition: Int) -> Int {
    return currentPosition - 1
}

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

var position: Int = 3   // 현 위치

let moveToZero: MoveFunc = functionForMove(position > 0)
print ("원점으로 갑시다.")
// 원점에 도착하면(현 위치가 0이면) 반복문 종료
while position != 0 {
        print("\(position)...")
        position = moveToZero(position)
}
print("원점 도착!")
//3... 2... 1... //원점 도착!

  • 중첩 함수의 사용
typealias MoveFunc = (Int) -> Int

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

    return shouldGoLeft ? goLeft : goRight
}

var position: Int = -4   // 현 위치
// 현 위치가 0보다 작으므로 전달되는 인자 값은 false가 됨
// 그러므로 goRight(_:) 함수가 할당됨

let moveToZero: MoveFunc = functionForMove(position > 0)
// 원점에 도착하면(현 위치가 0이면) 반복문이 종료됨
while position != 0 {
    print(":\(position)...")
    position = moveToZero(position)
}
print("원점 도착!")
// -4... -3... -2... -1... 원점 도착!


4. 종료되지 않는 함수

  • 종료되지 않는다는 말은 정상적으로 끝나지 않는 함수라는 뜻이다.
  • 비반환 함수(Nonreturning funcion) 또는 비반환 메서드(Nonreturning method)라고 한다.
  • 이 함수를 실행하면 프로세스 동작이 끝난 것과 같다.
  • 오류를 던진다든가, 중대한 시스템 오류를 보고하는 등을 한 후 프로세스를 종료한다.
  • 비반환 함수는 어디서든지 호출이 가능하다.
  • 비반환 메서드는 재정의 할 수 있지만 비반환 타입은 변경 불가하다.
    • 비반환 함수(메서드)는 반환 타입을 Never라고 명시해주면 된다.
      • Never 타입이 스위프트 표준 라이브러리에서 사용되는 대표적인 예로는 fatalError 함수가 있다.

  • 비반환 함수의 정의와 사용
func crashAndBurn() -> Never {
    fatalError("Something very, very bad happened")
}

crashAndBurn()  // 프로세스 종료 후 오류 보고

func someFunction(isAllIsWell: Bool) {
    guard isAllIsWell else {
        print("마을에 도둑이 들었습니다!")
        crashAndBurn()
    }
    print("All is well")
}

someFunction(isAllIsWell: true)
someFunction(isAllIsWell: false)


5. 반환 값을 무시할 수 있는 함수

  • @discardableResult
    • 의도적으로 함수의 변환 값을 사용하지 않을 경우 컴파일러가 함수의 결과 값을 사용하지 않았다는 경고를 보낼 수 있다. 이럴 때 @discardableResult를 사용하여 무시할 수 있다.
func say(_ something: String) -> String {
    print(something)
    return something
}

@discardableResult func discardableResultSay(_ something: String) -> String {
    print(something)
    return(something)
}

say("hello")
// 반환 값을 사용하지 않았으므로 컴파일러가 경고를 표시할 수 있으나
// @discardableResult 메서드를 사용해 경고 알림을 막음
profile
Let's smile for future 🤩

0개의 댓글