본문 바로가기

SWIFT 공식문서 문법 알아보기/05. Control Flow

Swift 공식문서 해설 Control Flow - Conditional Statements (5-2)

조건문 (Conditional Statements)

조건문은 특정 조건에 따라 다른 코드 조각들을 실행하기 위해 유용합니다. 오류가 발생하거나 값이 너무 높거나 낮을 경우 메시지를 보여주기 원할 때 코드가 실행되기 원할지도 모릅니다. 이런 경우 코드를 조건문으로 만들면 됩니다.

스위프트는 코드에서 2가지 조건문인 if문장과 switch 문장을 제공합니다. 일반적으로 if 문장은 몇 가지 결과만 나오는 간단한 조건문에 사용됩니다. swich 문장은 다양한 가능성의 순열이 나오는 복잡한 조건과 패턴 매칭으로 실행시킬 코드를 분리하는 상황에서 유용합니다. 

If

가장 간단한 형태로 단일 if 조건문이 있습니다. 이 조건문은 오직 조건식이 true 일 때만 문장을 실행합니다. 

 

var temperatureInFahrenheit = 30
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
}
// Prints "It's very cold. Consider wearing a scarf."

 

위의 예시는 기온이 32°F(물이 어는점) 보다 이하인지 여부를 확인합니다. 만약 이하로 내려간다면 메시지는 보이고 그렇지 않다면 어떠한 메시지도 없으며 코드의 실행은 if문장 괄호 다음부터 계속됩니다.

If는 if문장이 거짓일 상황일 때 else 절이라고 알려져 있는 대체 문장을 제공하고 있습니다. 아래 문장은 else 키워드를 이용해 만든 문장입니다.  

 

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."

 

2개 중 하나는 항상 실행됩니다. 왜냐하면 기온은 40°F로 올랐기 때문에 스카프를 착용하라고 충고할 만큼 춥지 않으므로 대신 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 절은 선택사항이며 만약 모든 조건 완벽하게 해당할 필요가 없다면 제외해도 상관없습니다.

 

temperatureInFahrenheit = 72
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.")
}

 

if 혹은 else if 조건식을 실행하기에 너무 춥지도 너무 따뜻하지도 않은 온도이기 때문에 어떤 메시지도 출력되지 않습니다. 

Switch

switch 문장은 몇몇의 패턴과 일치가 가능한 값을 고려하여 비교합니다. 그러고 나서 매칭에 성공한 첫 번째 패턴의 코드 블락을 적절하게 실행시킵니다. switch 문장은 다양한 가능성이 있는 상태에 대응하기 위해 if 문장의 대안으로 제공됩니다.

아래는 switch문장에 대한 간단한 형식입니다. switch 문장은 한 개 혹은 다수의 같은 타입의 값을 비교합니다.  

 

switch some value to consider {
case value 1:
respond to value 1
case value 2,
value 3:
respond to value 2 or 3
default:
otherwise, do something else
}

 

각 switch 문장은 다수의 case로 구성되어 있고 각각은 case라는 키워드로 시작합니다. 특정한 값을 비교하는 덧붙여 스위프트는 각 case에 더 복잡한 패턴을 매칭 하는 몇 가지 방법을 제공합니다. 이러한 기능은 다음 장 뒷부분에 설명되어있습니다. if 문장의 바디와 같이 각 case는 별도의 코드 브랜치를 실행시킵니다. switch 문장은 어떤 브랜치를 선택할지 결정합니다. 값을 고려하는 이 절차는 스위칭이라고 잘 알려져 있습니다. 모든 switch 문장은 철저해야 합니다. 그 말인즉슨 고려되는 모든 값들은 반드시 하나의 switch case와 매치되어야 합니다. 가능한 모든 값에 대해서 case 제공이 적합하지 않을 경우 명시적으로 할당되지 않은 어떤 값을 포함할 수 있도록 default를 제공하고 있습니다. 이 경우 default 키워드를 사용하고 반드시 마지막에 표시됩니다. 

 

아래 예는 someCharacter라고 불리는 단일 소문자를 고려하기 위해 switch 문장을 사용했습니다.

 

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 문장 첫 번째 case는 영어 알파벳 첫 번째 글자 a와 연결되며 두 번째 case는 마지막 알파벳 z와 연결됩니다. switch문장은 반드시 알파벳으로 된 문자 외에도 모든 가능한 문자가 case에 해당되어야 하기 때문에 위의 switch 문장은 a와 z 이외에 모든 문자들이 case에 일치되도록 defaul를 사용했습니다. 이와 같은 조항은 switch 문장의 완벽함을 보장합니다. 

No Implicit Fallthrough

C와 Objective-C와는 반대로 스위프트의 switch 문장은 fall through가 없어 각각의 case가 완료되지 않으면 default 다음으로 가지 않습니다. 그 대신 전체 switch 문장은 작성한 switch case에 처음으로 매칭 되면 특정한 break 문장 없이도 종료됩니다. 이러한 방식은 switch 문장을 안전하고 C언어보다 쉽게 사용할 수 있으며 실수로 인해 하나 이상의 switch case를 실행시키는 것을 피하게 만듭니다. 

 

*부연설명

C와 Objective-C의 경우 fall through라는 개념이 있습니다. swift의 경우 case의 구문이 비어있으면 실행이 안되거나 case구문이 여러 개가 매칭 된다면 가장 첫 번째 있는 case를 매칭 시키고 switch 구문이 종료되지만 C와 Objective-C 언어의 경우 break라는 키워드가 나오기 전까지 case를 계속 실행시킵니다. 이를 Implicit Fallthrough라고 하지만 swift는 오류를 방지하기 위해 이러한 방식을 사용하지 않습니다.

NOTE

break는 스위프트에서 요구하지는 않지만 특정한 case를 무시하거나 case의 실행이 완료되기 전에 매치된 case에서 빠져나올 때 break 문장을 사용할 수 있습니다. 자세한 내용은 Break in a Switch Statement을 참조

 

각각의 case안에는 반드시 실행 가능한 문구가 적어도 하나는 포함되어 있어야 합니다. 아래 예는 첫 번째 case가 비어있기 때문에 유요 하지 않은 코드입니다. 

 

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.

 

switch 문장은 C언어와 달리 위의 switch 문장은 "a"와 "A" 둘 다 매치되지 않습니다. 자세히 말하자면 "a"에 어떠한 실행 가능한 문장을 포함하고 있지 않기 때문에 컴파일 에러 오류를 보고합니다. 이러한 접근 방법은 하나의 case에서 다른 case로 우연한 fall through를 피하고 의도를 명확하게 하는 안전한 코드를 만듭니다. 

 

 "a"와"A" 둘 다 매치시키는 한 개의 case를 switch에서 만들기 위해서는 떨어진 값을 콤마를 통해 두 개의 값을 결합해서 compound case를 만들어야 합니다. 

 

let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
print("The letter A")
default:
print("Not the letter A")
}
// Prints "The letter A"

 

또한 가독성을 위해 compound case는 여러 줄에 걸쳐 쓰일 수 있습니다. compound cases에 관한 정보를 더 얻고 싶다면 여기를 보세요.

NOTE

명확하게 특정 switch case를 넘어가기 위해 Fallthrough에서 설명한 대로 fallthrough키워드를 사용하세요.

 

*부연설명

스위프트도 자동으로는 되지않지만 fallthrough 키워드를 사용한다면 가능

Interval Matching

switch case에서 값은 간격에 포함되어있는지 확인할 수 있습니다. 아래 예는 숫자의 간격을 어떤 크기의 숫자라도 언어로 계산되는 것을 제공하기 위해 사용합니다. 

 

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."

 

위의 예는 approximateCount는 switch 문장에서 평가됩니다. 각각의 case는 값을 숫자나 간격과 비교합니다. approximateCount 값은 12와 100 사이에 있기 때문에 naturalCount는 "dozens of"가 할당되고 실행은 switch 문장 밖으로 옮겨집니다. 

Tuples

같은 switch 문장에 다수의 값을 테스트하기 위해 tuples을 사용할 수 있습니다. 각 tuples의 요소들은 다른 값 혹은 값들의 간격에 대해 테스트할 수 있습니다. 대안으로 와일드카드 패턴이라고도 잘 알려진 언더바(_)를 사용하여 가능한 어떤 값과 매치시킬 수 있습니다. 

 

아래의 예는 (x, y) 점을 가져와 간단한 tuple 타입인 (Int, Int)로 표현했으며 예시에 따라 그래프에 분류합니다. 

 

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 문장에서 점이 (0, 0) 원점, 빨간색 x-axis 위, 오렌지 y-axis 위, 원점을 중심으로 파란색 4X-4 상자 안, 상자 밖에 있는지를 결정해줍니다. C언어와 달리 스위프트는 동일한 값 혹은 값을 고려하는 다수의 switch case를 허용합니다. 위의 예어서 사실 점 (0,0)은 4개의 모든 case에 일치됩니다. 그러나 다수의 매치가 가능한 경우 첫 번째 매칭 된 case만 항상 사용됩니다. 점 (0,0)은 첫 번째 case(0,0)과 처음 매치되었고 나머지 매칭 되는 case들은 모두 무시됩니다.

Value Bindings

switch case는 case의 바디에서 사용하기 위해  값 혹은 일시적으로 상수 혹은 변수가 case에 매치된 값의 지정할 수 있습니다. 이와 같은 행동을 binding이라고 알려져 있으며 값은 case의 바디 안에 일시적으로 상수와 변수를 묶을 수 있습니다.   

 

아래의 예는 (x, y) 점을 가져와 tuple 타입인 (Int, Int)로 표현했으며 예시에 따라 그래프에 분류합니다.

 

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 문장은 점이 빨간색 X축, 오렌지 y축, 혹은 그 밖에 어느 곳인지를 결정합니다. 

3개의 switch case는 상수 x, y를 선언하고 일시적으로 anotherPoint으로부터 한 개 혹은 모든 tuple 값을 차지합니다. 첫 번째 case (let x, 0)는 y의 값이 0이 매치되고 x값은 임시로 상수 x에 할당됩니다. 유사하게 두 번째 case (0, let y) 도 x값이 0에 매치되고 y값은 임시로 상수 y에 할당됩니다. 상수가 선언된 후 코드 안의 코드 블록을 사용할 수 있습니다.

switch 문장은 defaul case가 없습니다. 마지막 case let (x, y)는 어떤 값하고도 매치가 되는 두 개의 tuple 상수를 선언했습니다. anotherPoint는 항상 2개의 값을 가진 tuple이며 이와 같은 case는 모든 남아있는 값에 매치가 가능하기 때문에 완전한 switch 문장을 만들기 위해 defaultcase는 필요가 없습니다.

Where

switch case는 where 절을 사용해서 추가적으로 조건식을 확인하기 위해 사용합니다. 

아래의 예는 그래프에서 (x, y) 점을 분류합니다.

 

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 문장은 점이 x == y의 초록색 대각선의 라인, x == -y의 보라색 라인, 혹은 둘 다 아닌지를 결정합니다. 

3개의 switch case는 상수 x, y를 선언하고 yetAnotherPoint으로부터 일시적으로 2개의 튜플 값을 대신합니다. 이 상수는 역동적인 필터를 만들기 위해 where절의 일부로서 사용합니다. switch case는 현재의 포인트 값이 where 절의 조건식 평가가 참이어야 지만 매치가 가능합니다. 직전의 예와 마찬가지로 마지막 case는 남아있는 모든 값과 매치가 가능하기 때문에 완전한 switch 문장을 만들기 위해 defaultcase는 필요가 없습니다,

Compound Cases

같은 바디를 공유하는 다수의 switch case는 각각의 패턴 사이에 콤마를 사용하여 몇몇의 패턴을 결합시킬 수 있습니다. 어떤 패턴이라도 매치가 된다면 그 후 case는 매치되었다고 생각합니다. 만약 리스트가 길다면 패턴은 여러 줄로 쓸 수 있습니다.

 

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"

 

switch 문장의 첫 번째 case인 영어의 5개 소문자 모음과 일치합니다. 마찬가지로 두 번째 case는 모든 영어 소문자 자음과 일치합니다. 마지막으로 default case는 다른 모든 문자와 일치합니다.  

compound case는 binding 값도 포함할 수 있습니다. compound case의 모든 패턴은 설정이 동일한 값의 binding이 포함되어 있어야 하고 각각의 binding은 compound case의 모든 패턴으로부터 같은 타입의 값을 얻어야 합니다. 이를 통해 어떤 compound case와 일치하더라도 바디의 코드 case는 항상 바인딩 값에 접근이 가능하며 값은 항상 동일합니다.

 

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"

 

위의 case는 x축(let distance, 0) 점에 매치되거나, y축(0, let distance)에 점이 매치되거나 2가지 패턴이 있습니다. 두 가지 패턴 모두 바인딩에 distance를 포함하고 있고 distance는 2가지 패턴 모두에서 정수로 그것은 바디의 case가 항상 distance 값으로 접근이 가능하다는 의미입니다.