Swift 문법 CH.5_1

김성환·2020년 8월 22일
0

swift문법

목록 보기
5/11

안녕하세요. 본 포스트에서는 Swift의 함수에 대해 설명하겠습니다.

함수부분은 양이 많아 2부로 나눠 진행하겠습니다.
※ 참고자료 : 꼼꼼한 재은 씨의 Swift:문법편(타언어의 기본적인 개념은 생략합니다.)

목차

  • 1. 함수의 기본 개념
  • 2. 매개변수
  • 3. 일급객체로서의 함수
  • 4. 클로저

1. 함수의 기본 개념

사용자 정의 함수

함수를 정의하기 위해서는 func 라는 키워드를 사용한다.

func 함수이름(매개변수1: 매개변수1의 타입, ...) -> 반환타입{
	실행내용
    return 반환값
}

함수는 입력값을 받아 내부 처리 과정을 거친 후 그 결과값을 내놓는 형태를 기본으로 한다.
이때, 입력값을 매개변수 혹은 인자값 이라고 하고, 내놓는 결과값을 반환값 혹은 리턴값 이라고 한다.

  • 입력받을 매개변수를 작성시 :(클론)을 이용해 매개변수이름 과 그 매개변수의 타입을 명시해주어야 한다.
  • 함수에 결과값이 있을 경우 -> (화살표)를 이용해 그 결과값의 타입을 명시해주어야 한다.
  • 함수는 아래의 예시처럼 매개변수와 반환값이 있을 수도 없을 수도 있다.
    타 언어에서는 void 라는 것을 사용하는데 Swift는 void를 생략해도 된다.
func 함수이름( ) -> 반환타입{ // 매개변수가 없지만 반환값은 있는 경우
	실행내용
    return 반환값
}
func 함수이름( ){ // 매개변수도 없고 반환값도 없는 경우
	실행내용
}func 함수이름(매개변수1: 매개변수1의 타입){ // 매개변수는 있고 반환값은 없는 경우
	실행내용
}

함수의 호출

함수호출은 ()인 함수호출연산자를 사용한다.

  • 주의사항
    매개변수가 있는 함수를 호출 시 인자레이블과 들어갈 변수,상수를 명시해야한다.

인자레이블이란?

함수의 호출 시 인자값을 구분하기 위해 사용되는 레이블이다.
만약, 인자레이블을 누락할 경우 컴파일 오류가 발생한다.
인자레이블은 매개변수이름과 동일하다.
아래의 c++ 언어 사례로 이해해보자.

int f(int a){
	print(a)
}
// c++에서 f함수를 호출 시
f(1)
// 만약 swift식으로 호출할 경우
f(a : 1) // 인자 레이블이 a이기 때문에

따라서 함수의 이름을 명확하게 알 필요가 있다.
우리가 생각하는 함수의 이름은 순수하게 함수가 불려지는 이름이었다. 하지만 swift에서는 순수한 이름과 더불어 인자레이블을 포함한 것도 함수의 이름이라고 생각해야 한다.
그렇기 때문에 함수를 호출 시 2가지 방법이 있는 것이다.

함수이름(인자레이블 : 들어갈 값)
함수이름(인자레이블:)(들어갈 값)
  • 함수이름(인자레이블 : 들어갈 값) => 이것은 함수이름을 순수하게 그 함수가 불려지는 이름으로 보고 함수를 호출한 사례이다.
  • 함수이름(인자레이블:)(들어갈 값) => 이것은 함수이름을 인자레이블을 포함한 것까지 보고 함수를 호출한 사례이다. 즉, 함수이름(인자레이블:) 이것이 함수이름이고, (들어갈 값) 이것을 이용해 함수를 호출한 것이다.
    아래는 예시이다.
func times(x: Int, y : Int) -> Int{
	return (x + y)
}
times(x: 5, y: 10) // 첫번째 방법
times(x:y:)(5,10) // 두번째 방법 여기서 인자레이블이 여러개여도 ,(콤마)사용 안함

함수의 반환값과 튜플

튜플을 이용하면 여러개의 반환값을 한번에 반환할 수 있다.
이때. 타입 알리어스를 사용하면 간편하게 튜플을 이용할 수 있다.
(튜플에서 설명했기 때문에 예시만)

func getInfo() -> (Int,String) { // 반환값이 튜플이다.
	return (1, 2) // 실제 반환값도 튜플이다.
}

타입 알리어스(typealias)이란?

길거나 사용하기 복잡한 타입 표현을 새로운 타입명으로 정의해 주는 문법
typealias 라는 키워드를 사용한다.

typealias 새로운 타입 이름 = 타입 표현
-----------------------------------
typealias getInfoType = (Int,String)
func getInfo() -> getInfoType { // typealias를 이용해 새로운 타입으로 적용한 모습
	return (1, 2)
}

2. 매개변수

매개변수의 종류

함수에서 사용되는 매개변수에는 2가지 종류가 있다.

  • 내부 매개변수
  • 외부 매개변수

내부 매개변수란?

함수가 내부적으로 인자값을 잠조하기 위해 사용하는 변수
즉, 함수의 내용을 실행할 때 함수의 인자값을 참조할 때 사용하는 변수라는 뜻이다.

외부 매개변수란?

함수 외부에서 함수나 인자값을 구분하기 위해 사용하는 변수
즉, 함수를 호출할 때 인자 레이블로 사용하는 변수라는 뜻이다.

// 1은 내부 매개변수, 2는 내부 매개변수의 타입, 3은 외부 매개변수
func f(1 : 2){
	print(1)
}
// 함수이름 f(1:)
func f(3 1 : 2){
	print(1)
}
//함수이름 f(3:)
  • 외부 매개변수에 _(언더바)를 사용하면 함수 호출 시 인자 레이블을 생략할 수 있다.
    즉, 기존의 언어들과 같이 함수를 호출할 수 있다는 뜻이다.(f(1) 이런식으로)
    __
    (언더바)를 사용하는 의미는 외부 매개변수와 내부 매개변수를 분리하지만 외부 매개변수를 사용하지 않겠다는 뜻이다.
func f(_ a : Int){
	print(a)
}
f(1) // 함수 f에 리터럴 1을 넣은 경우
f(_:)(1) // 이때의 함수명은 f(_:)이다. 하지만 _를 생략할 수 있기 때문에 위의 식이 가능 

가변인자

일반적으로 함수는 미리 정의된 형식과 개수에 맞는 인자값만 처리하지만, 때에 따라 가변적인 개수의 인자값을 입력받아야 할때가 있는데 swift는 이러한 가변인자를 제공한다.
가변인자를 사용하려면 .(점) 3개를 사용해야 한다.

func 함수이름(매개변수명 : 매개변수 타입 ...)

밑에는 가변인자를 사용하는 예시이다.

func agv(score : Int ...){
	함수 내용
}
avg(score : 1,2,3,4) // 함수호출

기본값을 갖는 매개변수

매개변수에 기본값을 집어 넣을 수도 있다.

func 함수이름(매개변수명 : 매개변수 타입 = 기본값){
	함수 내용
}

매개변수가 아닌 매개상수

사실 swift에서는 매개변수가 우리가 생각하는 변수가 아니라 상수이다. parameter 즉, 매개변수가 parameter의 뜻이기 때문에 그렇게 부른 것이지 swift상에서는 매개상수이다.
c언어, c++언어에서는 함수가 인자를 받을 때 값에 의한 전달로 인자(뒤에서 배움)를 받는데 이때, 인자값을 복사해 그 복사된 값을 새로운 변수에 할당하는 형식으로 값에 의한 전달이 된다.
즉, int f(int a) 라는 함수가 있을 때, f(1)을 할 경우 1이 값에 의한 전달이 될 때 int a라는 함수 안에서 생존하는 임시 변수가 생성이 되고(실제로 메모리를 할당받음) 1이 복사가 되면서 a에 1이 들어가게 된다.
하지만 swift에서는 함수 안에서 생존하는 임시 변수가 생성이 되는 것이 아니라 임시 상수가 생성이 되어 상수 안에 복사된 인자값이 들어가게 되는 것이다.

결론은 함수 안에서 매개변수의 값을 변경할 경우 그 매개변수는 사실 상수이기 때문에 변경이 불가능하다.

func incrementBy(base: Int) -> Int {
	base += 1 // 오류!! base는 상수이기 때문에 값을 바꿀 수 없다.
    return base
}

위의 식을 올바르게 사용하려면 변수를 이용하면 된다.

func incrementBy(base: Int) -> Int {
	var base = base // 같은 이름으로 변수를 새로 선언과 동시에 초기화
    	//this.base = base인 것
    	base += 1 // 정상!!
    	return base
}

함수에 인자를 전달하는 방법

  • 값에 의한 전달
  • 참조에 의한 전달

값에 의한 전달

함수에 인자값을 전달하면 내부적으로 값의 복사가 이뤄지는데 그 복사된 값을 이용하여 구문을 실행하는 것
즉, 함수에 리터럴 대신 함수 밖에 선언된 변수를 집어 넣을 경우 그 값을 늘리고 줄이더라도 그 변수의 값에는 영향이 없다는 뜻이다.

참조에 의한 전달

함수에 인자값을 변수나 리터럴이 아닌 주소를 전달하는 것
즉, 함수에 함수 밖에 선언된 변수의 주소를 집어 넣을 경우 그 값을 늘리고 줄이면 그 변수의 값에 영향을 미친다는 뜻이다.
왜냐하면 그 변수가 위치한 메모리 주소를 함수에 넣어 주기 때문이다.
swift에서는 InOut 매개변수를 이용해 참조에 의한 전달을 할 수 있다.

InOut 매개변수

InOut 매개변수를 이용하면 주소를 전달할 수 있다.
inout 키워드를 사용하며 매개변수 타입 앞에 붙여서 사용한다.

func 함수이름 (매개변수이름 : inout 매개변수 타입)

inout이 붙은 매개변수를 사용하기 위해서는 주소추출연산자인 &을 이용해야한다.
예시를 통해 살펴보자

func foo (paramCount: inout Int) -> Int {
	paramCount += 1 // 정상작동!!
    return paramCount
}
var count = 30
print(foo(paramCount: &count)) // 함수 호출
print(count)
// 실행결과
31
31

위의 예시를 살펴보면 매개변수가 아니라 매개상수인데도 불구하고 매개상수의 값이 변하는 것을 볼 수 있다. 하지만, 매개상수가 변하는 것이아니라 count 변수의 값이 변하는 것이다.
왜냐하면 inout키워드를 이용했기 때문에 count 값이 복사되어 새로운 임시 상수가 생성된 것이 아니라 count 주소가 전달 되었기 때문이다.
즉, paramCount이라는 것은 inout이 붙은 Int 타입으로 c언어,c++에서 이용 되는 포인터변수라는 뜻이다.

따라서 foo(paramCount: &count)의미는

paramCount = &count라는 의미이고 이는 곧 paramCount(주소만 들어갈 수 있는 포인터변수)에 count 주소가 들어갔다는 뜻이다.

profile
개발자가 되고 싶다

0개의 댓글