Swift-Language Guide 5.7 / Control Flow (야매 번역 + 정리)

Newon·2022년 7월 7일
0

Swift-Language Guide 5.7

목록 보기
4/7
post-thumbnail

초록

Swift 는 다양한 제어 흐름을 제공합니다. 제어 흐름에는 for-in, while, if, guard, switch 등과 이를 조절하는 break, continue 등이 있습니다.

For-In Loops (For-in 반복)

For-in 반복 구문은 배열이나 특정 범위의 숫자, 혹은 문자열 내 캐릭터를 반복하는데 사용됩니다.

let names = ["안나", "알렉스", "브라이언", "잭"]
for name in names {
    print("안녕, \(name)!")
}
// 안녕, 안나!
// 안녕, 알렉스!
// 안녕, 브라이언!
// 안녕, 잭!

For-in 구문은 딕셔너리 역시 반복하여 접근할 수 있습니다. 딕셔너리의 원소들은 (key, value) 튜플로 반환되므로, (key, value) 를 for-in 반복 구문에 지정함으로써 이를 응용하여 사용할 수 있습니다. 단, 딕셔너리는 그 자체로 비정렬 컬렉션(Collection) 이기 때문에, for-in 반복 구문의 순서가 언제나 일정하지는 않습니다.

let numberOfLegs = ["거미": 8, "개미": 6, "고양이": 4]
for (animalName, legCount) in numberOfLegs {
    print("\(animalName)\(legCount)개의 다리가 있어요.")
}
// 고양이는 4개의 다리가 있어요.
// 개미는 6개의 다리가 있어요.
// 거미는 8개의 다리가 있어요.

for-in 구문을 통해 숫자 범위를 반복할 수도 있습니다.
이때 범위를 닫힌 범위로 사용하고 싶다면 ..< , ..> 를 사용할 수 있으며
열림 범위로 사용하고 싶다면 ... 를 사용할 수 있습니다.

for index in 1...5 {
    print("\(index) 곱하기 5 는 \(index * 5)")
}

// 1 * 5 는 5
// 2 * 5 는 10
// 3 * 5 는 15
// 4 * 5 는 20
// 5 * 5 는 25

let minutes = 60
for tickMark in 0..<minutes {
    print(tickMark, terminator: " ")
}
// 1 2 3 4 ..... 59

for-in 구문에서 사용되는 반복 지정자 (index) 는 for-in 구문 내에서 정의하여 사용할 수 있으며, let 키워드 없이도 상수로써 선언됩니다.
반복 지정자가 필요없다면 _ 를 사용하여 무시할 수도 있습니다.

let base = 3
let power = 10
var answer = 1
for _ in 1...power {
    answer *= base
}
print("\(base)\(power)제곱은 \(answer)")
// Prints "3의 10제곱은 59049"

for-in 구문에서 반복 연산자에 특정 조건을 붙이고 싶다면 stride(from:to:by:) 혹은 stirde(from:through:by:) 를 사용할 수 있습니다.

let minutes = 60
let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
    // 5분 단위로 반복하되, to 이전까지만 반복
    // (0, 5, 10, 15 ... 45, 50, 55)
}
let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
    // 3 단위로 증가하며, through 를 포함해서 반복
    // (3, 6, 9, 12)
}

While

while 반복문은 조건이 성립될 때 까지 구문을 반복으로, 다음과 같은 일반적인 형태를 갖습니다.

Swift Language Guide - Control Flow - While 에서
주사위 놀이를 들어 while 을 설명하는데, 이는 생략하였습니다.
확인하기

whilerepeat-while 형태로도 사용할 수 있습니다.
일반적인 형태는 다음과 같습니다.

Conditional Statements(조건 구문)

Swift 는 ifswitch 2개의 조건 구문을 제공합니다.
if 문은 상대적으로 짧은 구문에,
switch 문은 상대적으로 길거나 복잡한 구문에 적합합니다.

if

if 구문은 주어진 조건이 참인 경우에만 실행되는, 분기를 만드는 조건문입니다.

var temperature = -5
if temperature <= 0 {
    print("밖이 매우 춥네요. 목도리를 입는게 어떨까요?")
}
// 출력: "밖이 매우 춥네요. 목도리를 입는게 어떨까요?"

if 구문 후 else 를 붙여서 다른 분기에 대한 실행구문을 설정할 수 있습니다. 이때 else 구문은 선택사항입니다.

var temperature = 10
if temperature <= 0 {
    print("밖이 매우 춥네요. 목도리를 입는게 어떨까요?")
} else {
	print("밖이 춥진 않네요. 티셔츠 입어요."
}
// 출력: "밖이 춥진 않네요. 티셔츠 입어요."

if 구문에서 여러개의 분기가 필요하다면 다음과 같이 사용할 수도 있습니다.

var temperature = 35
if temperature <= 0 {
    print("밖이 매우 춥네요. 목도리를 입는게 어떨까요?")
} else if temperature >= 30 {
	print("밖이 진짜 더워요. 자외선 차단제를 잊지마세요.")
} else {
	print("밖이 춥진 않네요. 티셔츠 입어요."
}
// 출력: "밖이 진짜 더워요. 자외선 차단제를 잊지마세요."

Switch

switch 구문은 특정 값이 여러개의 조건 중에서 참이 될 수 있는지 고려할 때 사용됩니다. 이때 특정 적합한 조건이 성립되면 해당 조건의 코드를 실행합니다.
switch 는 여러개의 상황을 고려해야 할 때 if 구문 대신 사용할 수 있습니다.

switch 구문의 case 들은 value 가 성립할 가능성이 있는 구문들입니다. 만약 valuecase 에 해당하면 그 구문에 해당하는 코드를 실행하게 됩니다.
한편 switch 는 반드시 해당 value 가 가질 수 있는 모든 상황을 고려해야하며, default 를 통해 case 에 해당하지 않는 값들에 대한 내용을 정의할 수 있습니다.

다음과 같은 예시를 들 수 있습니다.

let someCharacter: Character = "z"
switch someCharacter {
case "a":
    print("알파벳 첫번째 글자")
case "z":
    print("알파벳 마지막 글자")
default:
    print("알파벳들 중 하나")
}
// 출력: "알파벳 마지막 글자"

No Implicit Fallthrough(암시적으로 내려가지 않음)

switch 구문은 특정 case 가 성립되었을 때 해당 구문을 실행한 후 그 다음으로 내려가지 않습니다. 대신 처음으로 case 가 성립된다면 구문 실행 후 즉시 switch 구문에서 빠져나오게 됩니다. 이를 통해 switch 는 더욱 안정적이고 직관적인 사용을 제공합니다.

한편 switch 의 case 는 빈 구문으로 설정할 수 없습니다.
다음과 같은 예시를 들 수 있습니다.

let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a": // 불가능, 케이스가 비어있다.
case "A":
    print("The letter A")
default:
    print("Not the letter A")
}
// case "a" 가 빈 구문이므로 compile 에러를 발생시킨다.

switch 의 case 에서 여러개의 값을 확인해야 한다면 다음과 같이 활용할 수 있습니다. 이때 여러줄에 걸쳐서도 사용할 수 있습니다.

let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
    print("A 알파벳")
default:
    print("A 알파벳이 아님")
}
// Prints "A 알파벳"

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

Tuples

switch 구문에서 tuple 을 활용할 수도 있습니다. 이때 특정 튜플의 원소를 비교하고 싶지 않다면 _ 를 사용할 수 있습니다.

let somePoint = (1, 1)
switch somePoint {
case (0, 0):
    print("\(somePoint) 는 원점에 있습니다.")
case (_, 0):
    print("\(somePoint) 는 x 축에 있습니다.")
case (0, _):
    print("\(somePoint) 는 y 축에 있습니다.")
case (-2...2, -2...2):
    print("\(somePoint) 는 박스 안에 있습니다.")
default:
    print("\(somePoint) 는 박스 밖에 있습니다.")
}
// Prints "(1, 1) 는 박스 안에 있습니다."

이때 No Implicit Fallthrough 에서 확인하였듯, 상단의 case 가 먼저 성립되면 하단의 case 들은 실행도 되지 않고 무시됩니다.


tuple 에서 특정 튜플 원소를 변수로 사용하고 싶다면 다음과 같이 사용할 수 있습니다.

let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
    print(" \(x) 값을 가지며 x 축에 있음.")
case (0, let y):
    print(" \(y) 값을 가지며 y축에 있음.")
case let (x, y):
    print("(\(x), \(y)) 값을 가지며 x,y 축 안 어딘가에 있음.")
}
// Prints "2 값을 가지며 x 축에 있음."

이때 이 switch 구문은 default 가 없음에도 마지막 case 인 let (x, y) 가 모든 상황을 포괄하기 때문입니다.

Where

switch 구문의 case 는 where 를 활용하여 특정 조건을 더할 수 있습니다.

let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
    print("(\(x), \(y)) 는 x == y 선에 있습니다.")
case let (x, y) where x == -y:
    print("(\(x), \(y)) 는 x == -y 선에 있습니다.")
case let (x, y):
    print("(\(x), \(y)) 는 무작위 선에 있습니다.")
}
// Prints "(1, -1) 는 x == -y 선에 있습니다."

Compound Cases (복합 케이스)

switch 구문에서 복합 상황은 다음과 같이 표현할 수 있습니다.

let someCharacter: Character = "e"
switch someCharacter {
case "a", "e", "i", "o", "u":
    print("\(someCharacter) 는 모음이다.")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
     "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
    print("\(someCharacter) 는 자음이다.")
default:
    print("\(someCharacter) 는 자음도, 모음도 아니다.")
}
// Prints "e 는 모음이다."

복합 상황에서도 특정 원소에 이름을 선언할 수 있습니다.

let stillAnotherPoint = (9, 0)
switch stillAnotherPoint {
case (let distance, 0), (0, let distance):
    print("축에 있으며, 원점으로부터 \(distance) 멀다.")
default:
    print("축에 있지 않다.")
}
// Prints "축에 있으며, 원점으로부터 9 멀다."

Control Transfer Statements (생략 구문)

생략에는 다음과 같은 키워드들을 사용할 수 있습니다.

  • continue
  • break
  • fallthrough
  • return
  • throw

continue 는 반복 구문에서 사용할 수 있으며
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 는 반복 구문에서 사용할 수 있으며
반복구문을 빠져나옵니다.

fallthrough 는 switch 구문에서 사용되며,
case 에서 사용 시 계속해서 다음 case 를 찾아서 실행하게 됩니다.

let integerToDescribe = 5
var description = "숫자 \(integerToDescribe) 는"
switch integerToDescribe {
case 2, 3, 5, 7, 11, 13, 17, 19:
    description += " 소수이며,"
    fallthrough
default:
    description += " 자연수이다."
}
print(description)
// Prints "숫자 5 는 소수이며, 자연수이다."

Labeled Statements(라벨 구문)

중첩 구문을 사용하면 복잡한 조건 구문도 설계할 수 있습니다. 단 이때 continue, break 등을 사용하려면 불필요한 코드를 추가하게 될 수 있는데 이를 label 을 붙임으로써 해결할 수 있습니다.

gameLoop: while square != finalSquare {
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }
    switch square + diceRoll {
    case finalSquare:
        // diceRoll will move us to the final square, so the game is over
        break gameLoop
    case let newSquare where newSquare > finalSquare:
        // diceRoll will move us beyond the final square, so roll again
        continue gameLoop
    default:
        // this is a valid move, so find out its effect
        square += diceRoll
        square += board[square]
    }
}
print("Game over!")

Early Exit

guard let 을 활용하여 필요하지 않은 정보가 조건에 맞지 않는 구문이 왔을 때 해당 함수를 빠르게 빠져나올 수 있습니다.

func greet(person: [String: String]) {
    guard let name = person["name"] else {
        return
    }

    print("안녕 \(name)!")

    guard let location = person["location"] else {
        print("오늘 너가 가는 길의 날씨가 좋았으면 좋겠다.")
        return
    }

    print("오늘 너가 가는 \(location) 날씨가 좋았으면 좋겠다.")
}

greet(person: ["name": "존"])
// Prints "안녕 존!"
// Prints "오늘 너가 가는 길의 날씨가 좋았으면 좋겠다."
greet(person: ["name": "제인", "location": "쿠퍼티노"])
// Prints "안녕 제인!"
// Prints "오늘 너가 가는 쿠퍼티노 날씨가 좋았으면 좋겠다."

Checking API Availability

Swift 는 API 를 활용할 수 있는지 확인할 수 있는 기능을 내장하고 있으며, 이를 통해 사용할 수 없는 API 를 호출하지 않게 할 수 있습니다.

if 혹은 guard 구문과 함께 사용하여 다음과 같이 활용할 수 있습니다.

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
}

@available(macOS 10.12, *)
struct ColorPreference {
    var bestColor = "blue"
}

// ------------------------------

func chooseBestColor() -> String {
    guard #available(macOS 10.12, *) else {
        return "gray"
    }
    let colors = ColorPreference()
    return colors.bestColor
}

// ------------------------------

if #available(iOS 10, *) {
} else {
    // Fallback code
}

if #unavailable(iOS 10) {
    // Fallback code
}
profile
나만 고양이 없어

0개의 댓글