기본 매개변수 값은 매개변수 목록에서 매개변수의 타입 애너테이션 뒤에 값을 할당하는 방식으로 함수의 어떤 매개변수에든 지정할 수 있어요. 기본 매개변수 값이 지정되면, 함수를 호출하는 쪽에서 그 매개변수를 생략할 수 있고 대신 기본값이 사용돼요.
func greeting(name: String = "guest", duration: Int = 2) -> String {
"Welcome, \(name). Enjoy your \(duration) night stay."
}
greeting(name: "Bobo", duration: 7)
// Returns "Welcome, Bobo. Enjoy your 7 night stay."
greeting(duration: 3)
// Returns "Welcome, guest. Enjoy your 3 night stay."
greeting(name: "Wynona")
// Returns "Welcome, Wynona. Enjoy your 2 night stay."
greeting()
// Returns "Welcome, guest. Enjoy your 2 night stay."
가변 매개변수를 사용하면 함수가 하나의 매개변수에 대해 지정된 타입의 값을 0개 이상 받을 수 있어요.
가변 매개변수는 매개변수의 타입 애너테이션 뒤에 점 세 개(...)를 붙여서 정의해요.
함수 본문 안에서 Swift는 이 값들을 지정된 타입의 배열로 사용할 수 있게 해줘요:
import Foundation
func geometricMean(_ numbers: Double...) -> Double {
var total = 1.0
for number in numbers {
total *= number
}
return pow(total, 1.0 / Double(numbers.count))
}
let result = geometricMean(1, 2, 3, 4, 5)
// result is 2.605171084697352
함수에 가변 매개변수 뒤에 이어지는 매개변수가 더 있으면, 가변 매개변수 다음에 오는 첫 번째 매개변수에는 반드시 명시적인 인자 레이블이 있어야 Swift가 가변 인자 목록이 어디에서 끝나는지 알 수 있어요.
함수 본문 안에서 매개변수는 변수가 아니라 상수로 취급돼요. 매개변수의 값을 바꾸려고 하면 컴파일 타임 오류가 발생해요. 함수가 매개변수의 값을 바꾸고 싶다면, 이 변경 가능성을 명시적으로 드러내기 위해 in-out 매개변수를 사용해야 해요.
in-out 매개변수를 사용하려면 프로그래머는 세 가지를 확실히 해야 해요:
:와 타입 이름 사이에 inout 키워드를 써서 in-out 매개변수를 명시적으로 표시해야 해요.&를 붙여서 수정할 수 있음을 명시적으로 표시해야 해요.이렇게 하면 함수는 in-out 매개변수의 내부 복사본을 만들고, 함수 본문에서 지시한 대로 수정한 다음, 함수가 반환될 때 수정된 값을 전달받은 변수에 다시 복사해요.
func updateVersion(_ record: inout (version: Int, title: String)) {
record.version += 1
}
var dbRecord = (version: 2, title: "Exercism")
updateVersion(&dbRecord)
// dbRecord is now (3, "Exercism")
in-out 매개변수와 관련해서 알아 두어야 할 추가 규칙이 몇 가지 있어요.
1. in-out 매개변수가 있는 함수 안에서는 in-out 매개변수로 전달된 변수를 참조할 수 없어요.
2. 같은 변수를 같은 함수의 여러 in-out 매개변수로 전달할 수 없어요.
예를 들어 `inoutFunc(&mutVar, &mutVar)`는 컴파일러 오류를 발생시켜요.
Swift에서는 함수를 다른 함수 안에서 정의할 수 있어요. 이런 함수를 중첩 함수라고 해요.
중첩 함수는 감싸는 함수를 위해 특정 작업을 수행하면서 바깥 스코프를 어지럽히지 않는, 일종의 비공개 도우미 함수로 흔히 사용해요:
func makeNumber(_ number: Int) -> Int {
func double(_ x: Int) -> Int {
x * 2
}
func increment(_ x: Int) -> Int {
x + 1
}
return number.isMultiple(of: 2) ? increment(double(number)) : double(number)
}
print(makeNumber(3)) // Prints 6
print(makeNumber(4)) // Prints 9
중첩 함수는 일반 함수처럼 동작하지만, 감싸는 함수의 본문 안에서만 보여요.
이 연습 문제에서는 좋아하는 요리책에 나오는 근사한 라자냐를 만들 수 있도록 코드를 좀 더 작성해 볼 거예요.
라자냐를 조리하는 데 걸리는 시간과 관련된 다섯 가지 과제가 있어요.
이전 과제에서 정의한 예상 오븐 시간(분)을 기준으로, 라자냐가 오븐에 들어간 후 지난 시간(분)을 매개변수로 받아 라자냐가 오븐에 얼마나 더 있어야 하는지 반환하는 개선된 remainingMinutesInOven 함수를 작성하려고 해요.
이전에는 remainingMinutesInOven 함수 바깥에 있는 expectedMinutesInOven이라는 상수 값에 의존했어요.
그런데 이 방식은 조금 복잡하게 느껴졌고, 예상 오븐 시간을 바꿀 수 없다는 점에서 유연하지도 않았어요.
깔끔하게 정리하기 위해, 라자냐가 오븐에 들어간 후 지난 시간(분)과 기본값이 40인 expectedMinutesInOven 매개변수를 받는 remainingMinutesInOven 함수를 정의해요.
이 함수는 이전 과제의 예상 오븐 시간(분)을 기준으로 라자냐가 오븐에 얼마나 더 있어야 하는지 반환해야 해요.
remainingMinutesInOven(elapsedMinutes: 30)
// Returns 10
remainingMinutesInOven(elapsedMinutes: 30, expectedMinutesInOven: 75)
// Returns 45
이전에는 라자냐에 추가한 층의 개수를 매개변수로 받아, 각 층을 준비하는 데 2분이 걸린다고 가정했을 때 라자냐를 준비하는 데 몇 분이 걸렸는지 반환하는 preparationTimeInMinutes 함수를 작성했었어요.
그런데 이 함수가 마음에 꼭 들지는 않아요. 이 함수를 호출하려면 라자냐의 층 개수를 직접 세어야 하니까요. 그럴 시간이 어디 있겠어요?
좀 더 편하게 하려면, 라자냐의 여러 층을 나열하는 가변 인자 String 매개변수 layers를 받는 preparationTimeInMinutes 함수를 작성해요.
그러면 이 함수가 라자냐의 층 개수를 세어 필요한 준비 시간을 반환해요.
preparationTimeInMinutes(layers: "sauce", "noodles")
// Returns 4
preparationTimeInMinutes(layers: "sauce", "noodles", "sauce", "meat", "mozzarella", "noodles", "sauce", "eggplant", "béchamel", "noodles", "sauce", "mozzarella")
// Returns 24
라자냐를 만들기 시작하기 전에, 꿈에 그리던 라자냐를 만들 소스와 면이 충분한지 확인하고 싶어요. 라자냐의 면 층마다 면 3개가 필요해요. 라자냐의 소스 층마다 소스 0.2리터가 필요해요.
라자냐의 여러 층을 나열하는 가변 인자 String 매개변수 layers를 받는 quantities 함수를 정의해요.
그러면 이 함수가 요리를 만드는 데 필요한 면과 소스의 양을 계산해요. 결과는 (noodles: Int, sauce: Double) 타입의 튜플로 반환해야 해요.
quantities(layers: "sauce", "noodles", "sauce", "meat", "mozzarella", "noodles", "sauce", "eggplant", "béchamel", "noodles", "sauce", "mozzarella")
// Returns (noodles: 9, sauce: 0.8)
quantities(layers: "sauce", "noodles", "sauce", "noodles", "meat", "noodles", "mozzarella")
// Returns (noodles: 9, sauce: 0.4)
안타깝게도, 가지고 있는 소스의 부피는 리터가 아니라 액량 온스로 표시되어 있어요.
소스가 충분한지 알 수 있도록, (noodles: Int, sauce: Double) in-out 튜플 매개변수를 받아 소스의 양을 액량 온스로 변환하는 toOz 함수를 작성해요.
1리터는 33.814 액량 온스라는 점에 유의해요.
var amount = (noodles: 9, sauce: 0.8)
toOz(&amount)
amount
// Returns (noodles: 9, sauce: 27.0512)
마지막으로, 라자냐와 함께 어떤 와인을 내야 할지 알고 싶어요.
라자냐에 고기와 소스보다 모차렐라, 리코타, 베샤멜이 더 많으면 화이트 와인을, 그렇지 않으면 레드 와인을 내야 한다고 들었어요.
라자냐의 여러 층을 나열하는 가변 인자 String 매개변수 layers를 받는 redWine 함수를 작성해요.
이 함수는 다섯 개의 중첩 함수를 사용해 라자냐에 있는 모차렐라, 리코타, 베샤멜, 소스, 고기 층의 개수를 구하고, 층 개수를 비교해 레드 와인을 내야 하는지 판단해서, 그렇다면 true를, 아니라면 false를 반환해요.
redWine(layers: "sauce", "noodles", "sauce", "meat", "mozzarella", "noodles", "sauce", "eggplant", "béchamel", "noodles", "sauce", "mozzarella")
// Returns true
redWine(layers: "sauce", "noodles", "béchamel", "meat", "mozzarella", "noodles", "ricotta", "eggplant", "béchamel", "noodles", "sauce", "mozzarella")
// Returns false
Exercism에 가입하고 Swift 트랙을 개념 35개연습 문제 116개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.