Swift 함수 - 2
가변개수 매개변수
매개변수의 개수를 여러개 받을 수 있다. 가령 여러 개의 정수를 받아 그 합을 반환해주는 함수를 선언한다고 생각해보자. 정수는 0개부터 엄청 큰 수까지 받을 수 있다
func addInt(_ ints:Int...) -> Int {
var ret = 0
for int in ints {
ret += int
}
return ret
}
print(addInt(1,2,3,4,5,6,7,8,9,10))
매개변수를 여러 개 받을 수 있도록 하려면, 매개변수의 타입 뒤에 0개 또는 그 이상을 받는따는 것을 의미하는 ... 를 사용하여 가변개수 매개변수를 선언할 수 있다
매개변수는 기본적으로 상수로 취급된다
Swift 는 기본적으로 함수형 패러다임을 따르는 언어다. 함수형 패러다임에서 함수는 부수 효과가 없어야 하는데, 따라서 변수의 값은 변경되면 안된다. 즉 매개 변수로 넘어오는 인자들은 기본적으로 상수로 취급되며, 함수 내에서 변경할 수 없다.
매개 변수가 상수로 취급되기 때문에, 함수의 실행이 외부에 영향을 미치지 않으며, 멀티 쓰레드 환경에서 안전하게 실행될 수 있다.
하지만 매개변수의 값을 변경 시키고 싶다면, 함수 내부에서 섀도 복사본을 생성해야 한다.
func calculateArea(length:Float, width:Float) -> Float {
var length = length //length 의 섀도 복사본 생성
var width = width //width 의 섀도 복사본 생성
length = length * 2.54
width = width * 2.54
return length * width
}
var length : Float = 1
var width : Float = 1
print(calculateArea(length : length, width : width))
print("\(length) \(width)")
위 함수는 인치를 받아서, 센티미터로 단위를 변환해 넓이를 구하는 함수다. 여기서 width 와 length 의 섀도 복사본을 생성해 계산을 수행한다. 하지만 섀도 복사본을 생성한다고 하더라도 원본인 length 와 width 에는 아무런 영향도 미치지 않는다.
즉 함수를 호출하고 나더라도 length 와 width는 초기값인 1로 유지된다.
매개변수에 대한 변경을 유지해보자
하지만 함수 호출 시 매개변수의 값을 변경하고 싶을 땐 어떻게 할까? 매개변수의 타입 앞에 inout 키워드를 붙이면, 함수 내부에서 해당 매개변수의 대한 변경이 원본에도 그대로 적용된다.
func multiplyTwo(value : inout Int){
value *= 2
}
var value = 50
multiplyTwo(value: &value)
print(value)
매개변수의 타입인 Int 앞에 inout 키워드를 붙임으로써 해당 함수 내에서의 매개변수 변경은 원본에도 적용된다. 이 때 주의할 점이 있는데, 함수 호출 시 매개변수 앞에 & 를 붙여야 한다는 것이다. C의 포인터와 매우 유사한 방법이다.
1급 객체로 취급되는 함수
함수형 프로그래밍 언어의 특징은 함수가 객체로 취급될 수 있다는 것이다. 즉 함수를 변수나 상수에 할당할 수 있고, 함수의 매개변수로 넘길 수도 있으며, 반환값으로 사용할 수도 있다.
이 때 함수의 타입이 중요하다. 우리가 함수를 함수 이름 뒤에 (매개변수) -> 반환타입 의 형식으로 선언했다. 즉 함수의 타입은 (매개변수) -> 반환타입이 되는 것이다.
만약 두 개의 정수를 받아서 어떤 작업을 수행한 뒤 아무 결과도 반환하지 않는 함수가 있다고 생각해보자. 이 함수의 타입은 (Int,Int) -> () 가 된다.
만약 아무 매개변수도 받지 않고 String 을 반환하는 함수가 있다면 이 함수의 타입은 () -> String 이 된다.
아래 예를 한 번 보도록 하자
func doSomething(function : (Int,Int) -> Int) -> Int {
function(5,10)
}
위 함수는 (Int,Int) -> Int 타입의 함수를 받아, 그 함수에 5와 10의 인자를 넘기고 그 결과를 반환한다. 이제 이것을 사용해보자. 매개변수로 Int 타입을 두 개 받고, Int 를 반환하는 어떤 함수도 이 함수의 매개변수로 넘길 수 있다.
func sub(a:Int,b:Int) -> Int {
a-b
}
func add(a:Int,b:Int) -> Int {
a+b
}
func mul(a:Int,b:Int) -> Int {
a*b
}
print(doSomething(function : add)) // 15
print(doSomething(function : sub)) // -5
print(doSomething(function : mul)) // 50
doSomething 함수에 매개변수로 더하기, 빼기, 곱하기를 수행하는 함수를 넘겼다.
클로저 표현식
클로저 표현식은 독립적인 코드 블록이다. 예를 들어, 다음은 클로저 표현식을 선언하고 그것을 sayHello 라는 이름의 상수를 할당한 다음에 상수 참조를 통해 함수를 호출한다.
let sayHello = { print("Hello") }
sayHello()
클로저 표현식은 매개변수를 받아 결괏값을 반환하도록 구성할 수도 있다. 다음의 구문을 살펴보자.
{(매개변수 이름 : 매개변수 타입 ...) -> 반환 타입 in
//클로저 표현식 코드
}
예를 들어, 다음의 클로저 표현식은 두 개의 정수를 매개변수로 받아 하나의 정수를 결과로 반환한다.
let multiply = {(_ val1:Int, _ val2:Int) -> Int in
return val1 * val2
}
let result = multiply(10,20)
이 구문은 함수를 선언할 때 사용하는 것과 비슷하지만, 클로저 표현식은 이름을 갖지 않으며, 매개변수와 반환 타입은 괄호 안에 포함되고, 클로저 표현식 코드의 시작을 가리키기 위하여 in 키워드를 사용한다. 즉 함수는 이름이 있는 클로저 표현식일 뿐이다.
이런 클로저 표현식은 비동기 메소드 호출 시 콜백 메서드로 주로 사용 된다.
eventstore.requestAccess(to: .reminder, completion : {(granted: Bool, error: Error?)
-> Void in
if !granted {
print(error!.localizedDescription)
}
})