프로그래밍에서 정의하는 ‘함수’란, 입력값 그리고 출력값을 가질 수 있는, 특정 기능을 수행하는 코드의 집합체이다.
다른 프로그래밍 언어와 마찬가지로, 스위프트에서도, 함수는 1) 정의(선언) 되는 단계, 그리고 2) 호출 되는 단계를 거쳐서 비로소 실행될 수 있다.
함수는 여러개의 입력값을 가질 수 있지만, 반드시 한개의 출력값(return 값) 만을 가질 수 있다.
함수를 사용하는 이유, 즉 함수 사용의 이점은 다음과 같다.
1) 반복되는 로직을 단순화하여 재사용 가능하다.
2) 코드를 논리적 단위로 구분 가능하게 한다. (→ 기능을 모듈화 할 수 있다.)
3) 길이가 긴 코드를 단순화 하기 좋다.
<case.1> input 과 output이 모두 없는 케이스
// 함수 선언
func caseOne(){
print("no input no output")
}
//함수 호출
caseOne()
<case.2> input 이 있고 output이 없는 케이스
//함수 선언
func caseTwo(a:Int, b:Int){
print(a+b, terminator: " ")
print(a-b, terminator: " ")
print(a/b, terminator: " ")
print(a*b)
}
//함수 호출
caseTwo(a:5,b:6)
<case.3> input 이 없고 output이 있는 케이스
// 함수 선언
func caseThree () -> Int {
let a = 5
let b = 6
return a*b
}
//함수 호출
print (caseThree())
<case.4> input 과 output이 모두 있는 케이스
//함수 선언
func caseFour (_ a : String, _ b : String) -> String {
return a+b
}
//함수 호출
print (caseFour("고양이 ", "귀여워"))
output이 없는 함수타입 (Void 타입 함수)은 다음과 같이 세가지 방식으로 표현가능하다.
func voidType1(){
print("hi")
}
func voidType2()->Void{
print("hi")
}
func voidType3()->(){
print("hi")
}
함수의 파라미터는 함수의 정의(선언) 과정에서 입력값으로 사용되는 변수(상수)를 말한다. 파라미터는 실제로는 상수 (let) 이기 때문에, 함수 안에서 그 값이 변경될 수 없다.
함수의 아규먼트는, 함수의 호출 과정에서 사용되는, 실제로 입력되는 값을 의미한다.
func caseTwo(a:Int, b:Int){
print(a+b, terminator: " ")
print(a-b, terminator: " ")
print(a/b, terminator: " ")
print(a*b)
}
caseTwo(a:5,b:6)
예를들어, 위 코드에서 함수 caseTwo의 선언에 사용된 변수 a,b는 이 함수의 파라미터 이며, caseTwo 함수 호출시 사용된 입력값 5, 6은 아규먼트 이다.
아규먼트 레이블이란, 함수의 파라미터를 외부에서 볼때, 함수 외부로부터 아규먼트를 표시하기 위해 사용하는 이름이다.
아규먼트 레이블은, 함수 선언시 파라미터 이름 앞에 적고, 띄어쓰기로 구분한다.
func plusFunction(number1 a:Int, number2 b:Int)->Int{
//코드 내용
}
위와 같은 함수가 있다고 할때, 아규먼트레이블은 number1, number2 이며, 파라미터 이름은 a, 그리고 b 이다.
func whatIsAL(a b : Int, c d : Int)->Int{
return b+d
} // a,c는 Argument Label, b,d는 Parameter Name
// 실제로 함수 내부에서 인자를 표현 / 연산 할때는 Parameter Name을 사용한다.
whatIsAL(a: 5, c: 4) // 함수 외부에서, 함수를 호출할때는 Argument Label을 사용.
아규먼트 레이블을 사용할때의 이점은 가독성과 편리성을 모두 챙길수 있다는 점이다.
다른사람들이 내 코드를 읽을때, 가독성이 좋으려면 함수 인자가 정확히 무엇을 뜻하는지 잘 표현하는 네이밍을 채택해야 하기 때문에 간혹 파라미터 이름을 길거나 복잡하게 짓게 될 수도 있다.
하지만 함수 내부에서 이 이름을 계속 사용하여 함수 내부 코드를 작성하는게 귀찮을 수도 있음 그럴때 아규먼트 레이블을 사용하면 함수 내부 코드를 구현할때 편리할것이다.
아규먼트 레이블을 사용했을때의 또다른 이점은, 아규먼트 레이블로 와일드카드 패턴을 채택하면, 아예 함수를 호출할때 아예 파라미터명을 생략할 수 있다는 점이다.
와일드카드 패턴이란, 사용되는 곳의 값을 무시(생략)하는 표현법이며, 스위프트에서는 언더바 (_)를 사용해서 와일드카드 패턴을 표현한다.
func anotherFunc(_ a : Int, _ b: Int) -> Void{
// Wildcard Pattern
print(a+b)
}
anotherFunc(4,5) // 파라미터 네임이 생략된 버전
// 주의 ~ 이렇게 파라미터 네임을 생략하려면, 함수 이름으로 부터 충분히 파라미터의 의미를 유추 가능해야 한다. ex) addTwoNum, printFullName 등
이 코드에서, 함수를 선언할때 아규먼트 레이블로 와일드카드패턴 (_)을 사용했으며, 그 결과 함수 호출시 별도의 아규먼트 레이블을 붙이지 않아도 된다.
이처럼 와일드카드패턴을 아규먼트레이블로 사용하면 함수 외부에서는 파라미터가 무엇을 의미하는지 확인할 수 없기 때문에, 함수명을 지을때, 어떤 파라미터를 넣어야 하는지 충분히 유츄 가능하게 짓는것이 중요하다.
(ex) addTwoInts, printFullName 등)
기본 파라미터란, 함수 선언시 파라미터의 값이 미리 초기화 되어 있는 상태의 파라미터를 의미한다.
func newSum(_ a : Int ,b: Int = 5)->Int{
return a+b
}
newSum(4)
이 코드에서 파라미터 b는 미리 5로 초기화 되어 있기 때문에, newSum(4)
와 같이 별도로 b의 아규먼트를 명시하지 않고 호출할 경우, b의 값은 자동으로 5가 된다.
newSum(4,b:6)
다만 디폴트 값은 처음에 초기화 된 값이기 때문에 위처럼 b의 아규먼트 값을 새롭게 명시해서 함수를 호출할 수도 있다.
기본 파라미터를 사용하는 대표적인 예시로 print 함수가 있는데, 실제로 print 함수의 파라미터는 items:Any
, **separator:String**
, **terminator:String**
의 총 세개이다.
함수 호출, 시, 세개의 파라미터 모두에 아규먼트를 명시해주는 예시이다.
print("jade","is","happy",separator: "🦋",terminator:" ")
실행 결과는 jade🦋is🦋happy
이며, “ ”로 종결하여다음 출력 값은 한칸 띄고 나타난다.
print("jade","is","happy")
실제로 자주 사용하는것처럼, print함수의 separator와 terminator 값을 명시하지 않으면, 디폴트 값인 separator = “ ”
, terminator = “\n”
이 적용된다.
가변 파라미터란, 하나의 파라미터로 두개 이상의 아규먼트를 전달 가능한 타입의 파라미터로, 아규먼트의 개수가 고정되지 않고 가변적인 파라미터이다.
가변파라미터를 사용할때는 범위 표현처럼, 파라미터의 타입 명 옆에 점 세개(…) 를 붙인다.
func arithmeticAverage(_ numbers : Double...) -> Double{
// numbers가 Double형 가변 파라미터
var sum = 0.0
for i in numbers {
sum += i
}
return sum / Double(numbers.count)
// Double 형태의 sum은 Double 값으로만 나눠질 수 있다.
// 따라서 numbers.count 는 Int 형태이므로, Double타입으로 타입캐스팅 해줘야한다.
}
print(arithmeticAverage(1,2,3,4,5,6,7,8,9,10))
// 파라미터의 개수가 정해져 있지 않기 때문에 파라미터는 한개이지만, 이처럼 여러개의 아규먼트를 받을 수 있다.
가변 파라미터와 관련된 규칙
1) 가변 파라미터는 배열의 형태로 아규먼트를 전달한다.
2) 가변 파라미터는 각 함수당 한개씩만 선언 가능하다. (파라미터가 여러개일때 선언되는 순서는 상관 없음‼️)
3) 가변파라미터는 기본파라미터로 사용할 수 없다 (초기화할 수 없다).
함수에서 리턴(return) 키워드의 활용은, 함수의 리턴타입 유무에 따라 다르다.
1) 함수의 리턴 타입이 있는 경우,
함수가 선언될 때 리턴타입이 명시되어있는 함수인 경우, 리턴 키워드 다음의 표현식을 평가한 후, 그 결과를 반환하며 함수 실행을 중지하고, 호출된 곳으로 돌아간다.
2) 함수의 리턴 타입이 없는 경우,
함수 실행을 중지하고, 함수를 벗어나 호출된 곳으로 돌아간다.
func printIfOdd (_ number : Int) {
if number % 2 == 0 {
print("even!!")
return // 함수 종료
}
else{
print("odd!!")
}
print("blahblah")
}
이 함수와 같이 리턴 타입이 없는 함수에서 return 키워드를 사용하면, 사용된 구간에서 함수가 바로 종료된다.
printIfOdd(2) // even!!
printIfOdd(3) // odd!!
// blahblah
중첩 함수는, 하나의 함수 안에서 다른 함수가 정의되고, 호출되는 형태의 함수로, 주로 한 함수를 조건에 따라 함수 특정 부분만 제한적으로 사용하고자 할때 사용한다.
func chooseStepFunction(gobackward : Bool, value : Int) -> Int {
// 함수 선언
func stepForward(_ input : Int) -> Int {
return input+1
}
func stepBackward(_ input : Int) -> Int {
return input-1
}
// 함수 호출
if gobackward {
return stepBackward(value)
}
else {
return stepForward(value)
}
}
이 함수 chooseStepFunction은 내부에서 새로운 함수의 선언과, 호출이 모두 이루어지는 형식이다.
if문을 사용해, ‘gobackward’라는 불리언 값에 따라 stepBackward(value)
혹은 stepForward(value)
을 호출하여, 결과 값을 반환한다.
var aValue = 7
chooseStepFunction(gobackward: false, value: aValue) // stepForward(aValue) 반환
chooseStepFunction(gobackward: true, value: aValue) // stepBackward(aValue) 반환