Swift 공식 문서의 다섯번 째 단원인 Control Flow (조건문, 반복문)를 읽고 정리를 해보려고 합니다.
Swift Apple 공식 문서 5챕터 Control Flow
for-in 루프 구문은 Array의 값들, String의 Character 값들과 같은 값들에 사용할 수 있다.
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
print("Hello, \(name)!")
}
// Hello, Anna!
// Hello, Alex!
// Hello, Brian!
// Hello, Jack!
위의 코드는 Array를 for-in 구문을 통해 사용해본 예시이다.
물론 Dictionary에도 사용할 수 있는데, 이때는 key-value 쌍에 접근할 수 있다.
이는 (key, value)의 튜플로 반환되게 되는데 이는 명시적으로 선언한 상수로 나누어 나타낼 수도 있다.
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
print("\(animalName)s have \(legCount) legs")
}
// cats have 4 legs
// ants have 6 legs
// spiders have 8 legs
Dictionary는 순서가 없는 타입이기 때문에 검색 순서가 보장되는 것은 아니다.
즉 삽입되는 순서가 for-in 구문에 의해 접근되는 순서는 아닐 수 있다는 것이다.
for-in 구문으로 숫자 범위에도 사용할 수 있다.
for index in 1...5 {
print("\(index) times 5 is \(index * 5)")
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25
만약 반복을 할 때 굳이 각각의 값들이 필요 없다면 아래와 같이 코드를 만들면 된다.
let base = 3
let power = 10
var answer = 1
for _ in 1...power {
answer *= base
}
print("\(base) to the power of \(power) is \(answer)")
// Prints "3 to the power of 10 is 59049"
_ 를 사용하여 for-in 구문을 사용하면 반복은 원하는 횟수만큼 해주지만 반복을 시작할 때마다 설정되던 상숫값을 만들지 않겠다는 말이다.
for-in 구문을 사용할 때 stride(from:to:by), stride(from: through: by:)
함수를 사용하면 반복을 몇 단계씩 건너뛰고 수행할 수 있다.
이때 stride(from:to:by)
는 to 값에 어디까지 수행하라는 뜻이고
stride(from: through: by:)
는 through 값까지 수행하라는 뜻이다.
let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
// render the tick mark every 5 minutes (0, 5, 10, 15 ... 45, 50, 55)
}
let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
// render the tick mark every 3 hours (3, 6, 9, 12)
}
While 루프는 조건이 거짓이 될 때까지 명령문을 수행하는 루프이다.
var age = 20
while age < 30{
print("현재 나이는 \(age)")
age += 1
}
사실 확실한 개념은 명령을 수행하고 조건을 확인하는 것이다.
var age = 30
repeat {
print("현재 나이는 \(age)")
age += 1
} while age < 30
즉 위와 같은 코드를 실행하면 조건이 age < 30인데 처음부터 age가 30이므로 기존의 while에서는 명령문이 한 번도 실행되지 않지만 repeat-while 구문에선 명령문을 수행 후 조건을 보기 때문에 한 번 수행 뒤 while 문이 종료되게 된다.
특정 조건에 따라 코드를 수행하는 것은 유용하다. 이렇게 조건에 따라 코드를 수행하게 만드는 것이 Conditional Statements이다.
if 문의 가장 간단한 모양은 if 하나만 사용한 것이다.
var temperatureInFahrenheit = 30
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
}
// Prints "It's very cold. Consider wearing a scarf."
위의 코드에서 temperatureInFahrenheit <= 32라는 조건을 만족하기 때문에 명령문이 수행된다.
만약 조건을 만족하지 않아서 명령이 수행되지 않아도 종료되는 것이 아닌 그 뒤의 코드들이 수행된다.
if는 else와 함께 사용할 수 있다. 이때 else에는 if의 조건이 false 일 때 실행하는 명령문이 들어간다.
temperatureInFahrenheit = 40
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
} else {
print("It's not that cold. Wear a t-shirt.")
}
// Prints "It's not that cold. Wear a t-shirt."
위의 코드를 보면 if의 조건이 false로 나오게 된다.
이럴 때 else가 있다면 else의 명령문을 수행하게 된다.
만약 조건을 여러 개 사용하고 싶다면 else if를 사용하면 된다.
temperatureInFahrenheit = 90
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 {
print("It's really warm. Don't forget to wear sunscreen.")
} else {
print("It's not that cold. Wear a t-shirt.")
}
// Prints "It's really warm. Don't forget to wear sunscreen."
이렇게 if-else를 사용할 때 마지막에 else만 사용하는 구문은 선택사항이며 제외할 수도 있다.
Switch 구문은 특정 값을 몇 가지 조건으로 비교하여 true로 나오는 조건들 중 첫 번째 패턴의 명령을 수행하는 구문이다.
가장 간단한 switch 구문은 switch와 case만 사용한 구문이다.
let someCharacter: Character = "z"
switch someCharacter {
case "a":
print("The first letter of the alphabet")
case "z":
print("The last letter of the alphabet")
default:
print("Some other character")
}
// Prints "The last letter of the alphabet"
switch에서 default 구문은 만약 값이 모든 조건에 false가 결과로 나오게 되면 수행될 명령을 선언하는 구문이다.
각각의 조건은 하나 이상의 실행문이 포함되어야 한다.
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a": // Invalid, the case has an empty body
case "A":
print("The letter A")
default:
print("Not the letter A")
}
// This will report a compile-time error.
❗ 위의 코드는 case "a" :의 조건에서 아무런 실행문이 없기 때문에 잘못된 코드이다.
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
print("The letter A")
default:
print("Not the letter A")
}
// Prints "The letter A"
위의 코드와 같이 여러 조건을 함께 사용할 수도 있다.
let approximateCount = 62
let countedThings = "moons orbiting Saturn"
let naturalCount: String
switch approximateCount {
case 0:
naturalCount = "no"
case 1..<5:
naturalCount = "a few"
case 5..<12:
naturalCount = "several"
case 12..<100:
naturalCount = "dozens of"
case 100..<1000:
naturalCount = "hundreds of"
default:
naturalCount = "many"
}
print("There are \(naturalCount) \(countedThings).")
// Prints "There are dozens of moons orbiting Saturn."
여러 값들을 같은 switch 구문에서 사용하고 싶다면 tuple을 사용하면 된다.
튜플의 각각의 원소는 각각 다른 조건에 의해 테스트 될 수 있다.
만약 튜플에 _를 사용하면 이는 wildcard pattern이라고 하며 어떠한 값도 허용한다는 뜻이다.
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
print("\(somePoint) is at the origin")
case (_, 0):
print("\(somePoint) is on the x-axis")
case (0, _):
print("\(somePoint) is on the y-axis")
case (-2...2, -2...2):
print("\(somePoint) is inside the box")
default:
print("\(somePoint) is outside of the box")
}
// Prints "(1, 1) is inside the box"
switch의 case에서 일시적인 상수나 변수를 선언해서 사용할 수 있다.
let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
print("on the x-axis with an x value of \(x)")
case (0, let y):
print("on the y-axis with a y value of \(y)")
case let (x, y):
print("somewhere else at (\(x), \(y))")
}
// Prints "on the x-axis with an x value of 2"
switch 구문에서는 where 절을 사용하여 추가 조건을 확인할 수 있다.
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
print("(\(x), \(y)) is just some arbitrary point")
}
// Prints "(1, -1) is on the line x == -y"
위의 코드와 같이 추가적인 조건을 주어 switch 구문을 사용할 수 있다.
,로 구분하여 여러 케이스들을 하나의 body와 공유할 수 있다.
let someCharacter: Character = "e"
switch someCharacter {
case "a", "e", "i", "o", "u":
print("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
print("\(someCharacter) is a consonant")
default:
print("\(someCharacter) is not a vowel or a consonant")
}
// Prints "e is a vowel"
물론 Compound Cases에서도 value binding을 사용할 수 있다.
let stillAnotherPoint = (9, 0)
switch stillAnotherPoint {
case (let distance, 0), (0, let distance):
print("On an axis, \(distance) from the origin")
default:
print("Not on an axis")
}
// Prints "On an axis, 9 from the origin"
Control transfer Statements는 코드가 실행될 때 다른 코드로 명령을 줘서 실행되는 순서를 바꾸는 역할을 한다.
continue 문은 루프가 수행 중인 작업을 중지하고 다음 반복의 시작 지점부터 다시 시작하라는 의미이다.
마치 현재 반복에서는 할 일을 다했으니 다음 반복을 수행하자라는 의미와 같다.
let puzzleInput = "great minds think alike"
var puzzleOutput = ""
let charactersToRemove: [Character] = ["a", "e", "i", "o", "u", " "]
for character in puzzleInput {
if charactersToRemove.contains(character) {
continue
}
puzzleOutput.append(character)
}
print(puzzleOutput)
// Prints "grtmndsthnklk"
break가 switch문에서 사용되면 switch문은 즉시 종료되고 바로 switch문의 마지막 글자인 "}"로 이동한다.
이러한 코드는 switch문에서 몇 가지 케이스를 무시할 때 사용할 수 있다.
Swift의 switch문은 케이스마다 반드시 실행문이 존재해야 하기 때문에 무시하고 싶은 케이스에 break를 사용하면 된다.
let numberSymbol: Character = "三" // Chinese symbol for the number 3
var possibleIntegerValue: Int?
switch numberSymbol {
case "1", "١", "一", "๑":
possibleIntegerValue = 1
case "2", "٢", "二", "๒":
possibleIntegerValue = 2
case "3", "٣", "三", "๓":
possibleIntegerValue = 3
case "4", "٤", "四", "๔":
possibleIntegerValue = 4
default:
break
}
if let integerValue = possibleIntegerValue {
print("The integer value of \(numberSymbol) is \(integerValue).")
} else {
print("An integer value could not be found for \(numberSymbol).")
}
let integerToDescribe = 5
var description = "The number \(integerToDescribe) is"
switch integerToDescribe {
case 2, 3, 5, 7, 11, 13, 17, 19:
description += " a prime number, and also"
fallthrough
default:
description += " an integer."
}
print(description)
// Prints "The number 5 is a prime number, and also an integer."
Swift에서는 다른 루프와 조건문 안에 또 다른 루프와 조건문을 중첩하여 복잡한 제어 흐름 구조를 만들 수 있다.
이렇게 만든 루프와 조건문은 break를 사용해서 조기에 종료할 수 있다.
이럴 때 종료할 루프와 조건문을 명시적으로 나타내 주는 게 효과적일 수 있다.
마찬가지로 continue문도 어떤 루프에 적용되는 것인지 명시하는것이 유용하다.
이러한 방법을 사용하기 위해 명령문 레이블을 사용하여 루프와 조건문을 표시할 수 있다.
명령문 레이블은 해당 루프 혹은 조건문 앞에 명시해 주면 된다.
var age = 20
var name = "Ick"
checkage: while age < 30{
if name == "Ick"{
break checkage
}
else{
age += 1
}
}
위의 코드와 같이 사용할 수 있다.
if문과 비슷하게 guard문도 조건의 결과에 따라 명령문을 실행한다.
func greet(person: [String: String]) {
guard let name = person["name"] else {
return
}
print("Hello \(name)!")
guard let location = person["location"] else {
print("I hope the weather is nice near you.")
return
}
print("I hope the weather is nice in \(location).")
}
greet(person: ["name": "John"])
// Prints "Hello John!"
// Prints "I hope the weather is nice near you."
greet(person: ["name": "Jane", "location": "Cupertino"])
// Prints "Hello Jane!"
// Prints "I hope the weather is nice in Cupertino."
만약 guard문의 조건이 true라면 guard문의 "}"부분 뒤의 코드들이 실행된다.
guard문에서도 binding을 사용할 수 있는데 이때 선언된 변수나 상수는 guard문이 선언된 나머지 코드에서도 사용할 수 있다.
만약 조건이 false라면 else문의 명령을 수행한다.
이때 else문의 명령은 코드를 종료하기 위해 return, break, continue, throw와 같은 제어 전송 명령문으로 작성되거나 fatalError(_: file: line:)
과 같이 리턴되지 않는 함수 또는 메서드를 호출해야 한다.
guard문은 조건이 반드시 true가 되어야 하는 곳에서 사용하면 좋다.
Swift는 API 사용 가능성 확인을 지원하기 때문에 실수로 사용할 수 없는 API를 사용하는 일을 방지할 수 있다.
컴파일러는 SDK의 사용 가능 정보를 사용하여 코드에 사용된 모든 API가 프로젝트에 지정된 배포 대상에서 사용 가능한지 확인해 준다.
만약 사용할 수 없는 API를 사용하려고 하면 컴파일 오류를 발생시킨다.
개발자는 if, guard 문을 사용해서 avilability condition을 사용할 수 있다.
if #available(iOS 10, macOS 10.12, *) {
// Use iOS 10 APIs on iOS, and use macOS 10.12 APIs on macOS
} else {
// Fall back to earlier iOS and macOS APIs
}
마지막 인수 * 는 필수이고 다른 플랫폼에서 if에서 지정한 최소 배포 버전 이상의 버전에서만 실행되도록 지정한다.