[swift] function, 함수에 대하여

이은수, Lee EunSoo·2024년 9월 3일
0

Swift Basic

목록 보기
8/24
post-thumbnail

함수란?

특정 작업을 수행하는 코드 모음

1 함수의 정의

함수는 파라메터를 가지고 반환값을 갖는다, 물론 둘다 없어도 된다.

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

이 형태가 가장 기본적인 함수의 형태이다

1.1 함수의 호출

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

호출은 다음과 같이 하며 함수를 호출할땐 파라메터의 이름을 명시하고 호출해야한다
(다른언어들의 경우 없어도 되지만 swift는 있어야 한다)

2 함수의 여러가지 형태

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

-> 기본적인 함수의 형태, 문자열 하나를 파라메터로 갖고, 문자열을 반환하는 함수

2.1 파라메터가 여러개 존재하는 함수

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

문자열 한개와 부울타입 파라메터를 갖는 함수

2.2 파라메터가 없는 경우

func greet() -> String {
    let greeting = "Hello, world"
    return greeting
}

print(greet())	//호출

그냥 호출만 하면 값이 반환된다.

2.3 반환값이 없는경우

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

다른 언어들의 경우 반환이 없는 경우 void를 사용하는데 swift의 경우 ->타입 을 작성하지 않는것으로 반환값이 없는것을 표현한다.

2.3.1 반환값을 무시하기

func greet() -> String {
    let greeting = "Hello, world"
    print(greeting)
    return greeting
}

let _ = greeting()

위 함수의 경우 함수 내부에서 print를 한번 해준다 따로 print를 해줄 필요가 없으므로 반환값을 버리는 방법을 보여준다.

let _를 사용해 반환값을 버린다.

평소에는 반환값을 받으며 사용하다가 특정 상황에 함수를 한번 실행만 하고 싶은때 사용하면 된다.

2.4 여러개의 값을 반환하기

방법은 바로 tuple을 반환하는것이다.

func someFunction(_ first: Int, _  second: Int) -> (Int, Int){
	return (first, second)
}

아래 코드 처럼 반환값에 라벨링을 할 수 있다 나중에(2.4.1) 보면 알겠지만 반환값에 접근할 때 이 라벨을 이용해서 접근할 수 도 있다.

func someFunction(_ first: Int, _  second: Int) -> (first: Int, second: Int)? {
	return (first, second)
}

반환타입이 tuple이기에 반환은 당연히 2개 이상의 값도 할 수 있다.

그리고 여러개의 데이터를 반환하고자 할때 무조건 tuple을 사용할 필요는 없다

상황에 따라서 컬렉션중에 하나를 반환(Array, Set, Dictoinary)하는것도 방법이 될 수 있겠다.(그렇게 할 경우 반드시 반환타입을 지금과는 다르게 지정해야 한다.)

2.4.1 옵셔널 튜플을 반환하기

tuple의 경우 옵셔널을 반환하는것을 지원한다.

func someFunction(_ first: Int, _  second: Int) -> (first: Int, second: Int)? {
	return (first, second)
}

반환타입에 ?를 붙여주기만 하면된다.

다음은 사용방법이다.

if let someResult = someFunction(10, 20) {
    print("first is \(someResult.first) and max is \(someResult.second)")
}

접근은 반환값에 라벨링한 이름으로 접근하면 된다.

2.5 암시적 반환

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

다음과 같이 한줄로 되어있는 함수의 경우 return을 생략할 수 있는데 이러한 함수형태를 암시적 반환이라고 한다.

3 argument label & parameter name

각 함수 파라미터는 인수 라벨 (argument label)파라미터 이름 (parameter name) 가진다.

애플에서 제공하는 문서의 설명을 살펴보자

인수 라벨 (argument label) 은 함수가 호출될 때 사용되고 각 인수는 함수 호출 시 인수 라벨 다음에 작성합니다.
파라미터 이름 (parameter name) 은 함수를 구현할 때 사용됩니다. 기본적으로 파라미터는 인수 라벨로 파라미터 이름을 사용합니다.

벌써부터 헷갈린다 ㅋㅋㅋ

인수 라벨은 함수를 부를때(사용할때)사용하고 파라메터 이름은 함수 내부에서 사용된다는 뜻이다

다음 코드를 살펴보자

func someFunction(argumentLabel parameterName: Int)->Int{
    return parameterName
}

print(someFuction(argumentLabel: 158))

이처럼 인수라벨 argument label은 외부에서 호출할때, 파라메터 이름 parameter name은 함수 내부에서 사용할때 사용된다.

func 함수이름(인수라벨, 파라메터이름: 타입)의 순서로 작성

Q. 왜 내부와 외부의 이름을 바꾸는 걸까?

사실 다른 언어들은 함수를 호출할때 인수라벨과 파라메터 이름을 구분하지 않는다

swift가 함수형 프로그래밍을 지원하기도 하고 때문에 함수를 1급객체로 취급할정도로 함수를 중요하게 생각하고 만들었기 때문이라고 생각된다.

Q. 함수형 프로그래밍이 뭔데?
A. 함수형 프로그래밍은 상태를 변경하지 않는 순수 함수를 사용해 프로그램을 구성하는 방식의 프로그래밍 방법

이렇게 인수라벨과 파라메터 이름을 명시하게 되면 호출하는 쪽에서도 헷갈리는 경우가 적어지고 함수를 구현하는 쪽에서도 코드의 가독성이 높아진다.

3.0.1 파라메터 이름의 생략

parameter name은 생략이 가능한데 이게 바로 우리가 알고 있는 함수의 형태이다 다음 코드를 보자

func someFunction(argumentLabel1:String, argumentLabel2 parameterName1: Int) -> String{
    return argumentLabel1 + "is \(parameterName1)"
}

print(someFuction(argumentLabel1: "who" ,argumentLabel2: 158))
//who is 158

다음과 같이 parameter name이 생략되면 argument label이 그 역할을 대신한다 argument label이 호출에도 사용되고 내부에서도 사용된다

우리가 흔히 사용하는 함수 형태는 인수라벨만 사용하는 형태인 단축형이었던 것이다.

3.1 인수라벨 생략

func someFunction(_ argumentLabel parameterName: Int)->Int{
    return parameterName
}

print(someFuction(158))

앞의방법이 너무 복잡한가? 이번에는 생략을 해보자!

_ 언더바를 사용하면 함수 호출시에 인수라벨(argument label)없이 호출하게 해준다.

주의할점은 *무조건 언더바 뒤에 공백(스페이스)를 넣어줘야 한다. 공백이 없으면 _person 을 하나의 이름으로 취급한다.

3.2 파라메터의 기본값

func someFunction(_ name: String = "hong gil dong")->String{
    return name
}

print(someFuction())

이런식으로 파라메터 뒤에 = 을 사용하고 원하는 값을 넣으면 된다.
(우리가 사용하는 swiftUI의 많은 컴포넌트들도 기본값을 사용한다.)

3.3 가변 파라메터

이것 또한 다른언어들에는 잘 없는 방법인데 파라메터를 받을때 배열등을 사용하는게 아니라 그냥 여러개의 데이터 전달을 지원한다 다음 코드를 보자

func totalNumber(_ numbers:Int...)->Int{
	var total: Int = 0
    for number in numbers {
        total += number
    }
    return total
}

print(totalNumber(1,2,3,4,5))

//결과: 15

이런 식으로 파라메터타입 뒤에 ... 점세개를 붙이면 여러개의 입력을 지원한다.

swift의 함수를 여러개의 가변함수를 지원한다. 여기서 주의할점은 가변파라메터 뒤에오는 파라메터에는 반드시 인수라벨이 작성되어야 한다는 점이다.

3.3.1 가변 파라메터인 numbers의 타입은 무엇일까?

func totalNumber(_ numbers:Int...)->Int{
	var total: Int = 0
    print(type(of: numbers))

    for number in numbers {
        total += number
    }
    return total
}

print(totalNumber(1,2,3,4,5))

//Array<Int>
//결과: 15

전의 코드에서 함수안에 numbers의 타입을 출력하는 코드를 넣었다

결과는 Array<Int> 였다.

이를 통해 swift가 가변 파라메터를 받으면 자동으로 배열을 만들어 주는것을 알 수 있다.

3.4 inout 키워드

함수에서 선언한 변수들은 모두 지역변수이다
(함수내부에서만 사용가능하고 외부에는 영향을 미치지 않는다.)

또한 swift함수의 인수 라벨은 함수내부에서 var가 아닌 let으로 처리된다.
(실제로 xcode에서 인수라벨 그대로 값을 변경하려고 하면 let이라 변경 할 수 없다는 오류가 나온다.)

그렇다면 swift에서 함수를 이용해 함수 밖의 데이터에 접근하고 싶다면 어떻게 해야 할까?
C언어의 경우 포인터가 있어서 파라메터로 메모리 주소값을 넘겨 받으면 함수에서 메모리 주소값으로 포인터를 이용해서 값을 변경 할 수 있다.

하지만 swift는 C언어와 달리 포인터를 지원하지 않는다.

그렇다면 함수로 함수 외부의 데이터를 변경할 수는 없을까?
이를 해결하기 위해서 swift에서는 inout 키워드를 지원한다.

마치 PL/SQL에서 사용하던 프로시저에서 IN,OUT,INOUT 키워드로 매개변수 받는 방법이 떠오른다.

다음 코드를 보자

var name:String = "eunsoo"
  
func changeName(_ tmpName: inout String){
  tmpName = "minsoo"
}

changeName(&name)
print(name)
  
//결과: minsoo

사용방법은 인수라벨: inout 타입 이런 형식으로 사용한다.

호출 할때 name변수앞에 &을 붙여주는것으로 name변수의 주소값을 넘겨준다.
(느낌은 C언어랑 비슷하긴 하다.)

inout키워드로 tmpName이 name변수의 일종의 포인터로 역할을 수행한다.
(&로 변수의 주소값을 넘겨받고 실제로 값을 변경하고 있으니...)

4.0 함수 타입

앞서 인수라벨과 파라메터 이름에 대해서 이야기 할때 swift에서 함수를 1급객체라는 말을 잠깐 한적이 있다.

무슨말이냐면 함수가 그 자체가 타입이 될 수 있고 파라메터나 반환값이 될 수 있다는 말이다.

코드를 보자

func add(_ numA: Int, _ numB: Int)->Int{
  return numA + numB
}

두개의 파라메터를 받아서 더한값을 출력하는 이 간단한 함수는 타입으로 보면 (Int, Int) -> Int 로 볼 수 있다.

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

이 함수는 파라메터 없이 반환도 없는 () -> Void 타입으로 볼 수 있다.

이처럼 함수를 타입으로도 볼 수 있다.

4.1 함수타입 활용

var mathFunction: (Int, Int) -> Int = add
var anotherMathFunction = add //이런식으로 타입추론도 가능하다.

다음과 같이 함수타입을 이용해 변수를 만드는것도 가능하다 이 mathFunction은 함수처럼 사용이 가능하다

print(mathFunction(10,20))

//결과: 30

4.2 함수타입을 파라메터로 사용하는 함수

함수를 파라메터로 넘길 수 도 있는데 다음 코드를 확인해보자

func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
  print("Result: \(mathFunction(a, b))")
}
printMathResult(add, 15, 25)

//결과: 40

이 함수는 3개의 파라메터를 받는다.

1. (정수두개를 입력받아 정수를 반환하는 함수),
2. (정수),
3. (정수)

이렇게 총 3개를 받는다.

두 정수를 더하는 (add함수)를 넘겨서 입력한 (두개의 정수)를 더해서 출력하는 함수이다.

4.3 함수타입을 반환하는 함수

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 가 반환타입인 함수이다.
-> 가 여러개라 헷갈린다면 함수에서 처음으로 -> 가 나온뒤에 모든것이 반환타입이라고 생각하면 편하다.

5 중첩 함수

함수내부에서 함수를 호출 할 수 있다 심지어 자기자신을 호출하는것도 가능하다.

func reclusive(_ input:Int){
    if (input < 10) {
        reclusive(input+1)
   }else{
       print(input)
       return
    }
}

reclusive(1)

이런식으로 반복문을 사용하지 않고 함수 중첩으로 반복을 할 수도 있다.

func add(_ numA: Int, _ numB: Int)->Int{
  return numA + numB
}
  
func twice(_ input:Int)->Int{
	return add(input, input)
}

twice(10)

//결과: 20

이런식으로 다름 함수를 외부에서 불러올 수도 있다.

참조
https://bbiguduk.gitbook.io/swift/language-guide-1/functions#defining-and-calling-functions

profile
iOS 개발자 취준생, 천 리 길도 한 걸음부터

0개의 댓글