본문 바로가기

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

Swift 공식문서 해설 Control Flow - For-In Loops, While Loops (5-1)

스위프트는 다양한 control flow 문장을 제공합니다. while loop는 어떤 일을 여러 번 시키는 데 사용하고 if, guard, switch 구문은 특정 상황에 따라 다른 코드를 실행시키며 break, continue와 같은 문장은 코드의 실행을 다른 지점으로 옮깁니다. 또한 스위프트는 for in 루프를 통해 배열, 사전, 범위, 문자열, 다른 연속적인 사건들을 쉽게 계산을 반복해서 처리할 수 있습니다. 

스위프트의 스위치 문장은 C와 같은 많은 언어에 대응해 꽤 강력합니다. Case는 interval matches, tuples, and casts to a specific type을 포함한 여러 가지 다른 패턴들과 연결시켜줍니다. switch case로 연결된 값은 일시적으로 case 문구 내에서 사용하기 위해 상수와 변수를 묶을 수 있고 복잡한 연결 상태의 경우 각 case를 where clause로 표현할 수 있습니다. 

For-In Loops

for-in loop를 사용하여 배열, 숫자의 범위, 문자열의 문자와 같이 반복적인 계산을 할 때 사용합니다. 

 

*부연설명

for <루프 상수> in <순회 대상> {
<실행구문>
}

위와 같은 구문의 구조를 가지고 있으며 순회대상에는 배열, 딕셔너리, 집합, 범위 데이터, 문자열이 있습니다. 5가지에 대한 자세한 설명은 Collection Types을 참조하면 됩니다.

 

아래 예는 for-in loop를 사용하여 배열의 항목들을 반복합니다. 

 

let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
print("Hello, \(name)!")
}
// Hello, Anna!
// Hello, Alex!
// Hello, Brian!
// Hello, Jack!

 

또한 key-value로 접근해서 dictionary를 반복할 수도 있습니다. dictionary가 반복될 때 각각의 dictionary안의 요소들은 tuple (key, value)로서 반환되고 tuple(key, value)의 요소들은 확실하게 지정된 상수에 분해되어 바디에서 for-in loop를 사용할 수 있습니다. 아래의 코드 예를 보면 dictionary의 키는 animalName이라는 상수에 분해되었고 dictionary의 값은 legCount라는 곳에 분해되었습니다. 

 

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의 상수는 선천적으로 무질서하고 이들을 반복해서 한다 해도 검색되는 순서가 보장되는 것은 아닙니다. 특히 dictionary에 항목을 넣는 순서는 이들이 반복되는 순서를 정의하지는 않습니다. arrays and dictionaries에 대해 더 알고 싶다면 Collection Types를 보세요

 

for-in loop는 숫자 범위에서도 사용합니다. 아래 예는 5의 배수의 목록을 보여줍니다.

 

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

 

위의 예시는 닫힌 범위 연산자(...)를 사용하여 1~5까지의 숫자 범위를 반복하여 계산한 것입니다. index의 값은 첫 번째 숫자 (1)로 설정되어있고 루프 내의 문장이 실행됩니다. 이 경우 루프는 현재 보이고 있는 전체 5개의 문장 중 index 값 하나의 문장만을 포함하고 있습니다. 문장이 실행된 후 index 값은 범위의 2번째인 (2)를 포함하여 업데이트되고 print 기능은 다시 실행됩니다. 이 과정은 범위의 값이 끝날 때까지 계속됩니다.

위의 예는 index는 각 loop가 처음 반복적으로 계산될 때 자동적으로 값이 지정되는 상수입니다. index는 사용하기 전에 선언을 할 필요가 없습니다. let 선언 키워드를 사용할 필요 없이 loop 내에 암묵적으로 선언이 포함되어있습니다.

 

만약 각 값이 필요가 없다면 변수명 대신 언더바(_)를 사용하여 값을 무시할 수 있습니다. 

 

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"

 

위의 예는 한 숫자의 값을 거듭제곱의 값을 계산해줍니다. (이 경우 3의 10 거듭제곱입니다) 시작 값 1에 10으로 끝나는 닫힌 범위를 사용해 시작 값 1 (즉 3의 0의 거듭제곱)에 3을 10배 곱합니다. 이 계산은 각각의 루프를 통과할 때마다 각각의 값이 필요하지 않고 코드는 단순히 루프를 정확한 횟수만큼 실행합니다. 언더바(_)를 변수명에 대신 사용하게 되면 개별 값은 무시되고 각 반복되는 루프의 현재 값은 제공하지 않습니다

 

어떤 상황에서는 두 끝점이 모두 포함된 닫힌 범위를 사용하고 싶지 않을 때도 있습니다. 시계 예 1분마다 눈금을 그린다고 생각해보자 0에서 60개의 눈금을 그리기를 원합니다. 하한은 포함하고 상한은 포함하지 않는 반 닫힌 연산자를 사용하세요. 범위에 대해 알고 싶다면 범위 연산자를 보세요

 

let minutes = 60
for tickMark in 0..<minutes {
// render the tick mark each minute (60 times)
}

 

*부연설명

1분마다 눈금을 그린다고 했을 때 0, 1, 2, 3, 4... 58, 59 가 됐을 때 60에서는 0과 겹치기 때문에 59까지 그리는 게 맞습니다. 0... <minutes라는 순회대상에서 비교 연산자(<) 기준 왼쪽 값까지 포함하고 오른쪽 값은 포함하지 않는 연산자를 반 닫힌 연산자라고 합니다. 

 

어떤 사용자는 그들의 UI에서 더 적은 눈금을 원할 수도 있습니다. 매 5분마다 눈금이 표기되기를 원할 수도 있습니다. stride(from:to:by:) 기능을 활용해서 원하지 않는 눈금을 넘기세요

 

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)
}

 

*부연설명

위 예시와 마찬가지로 5분 단위로 끊었을 때  0, 5, 10, 15... 50, 55에서 60과 0은 중복될 필요가 없기 때문에 반 닫힌 연산자를 사용했습니다. 

 

또한 닫힌 범위는 stride(from:through:by:)를 대신 사용하여 사용 가능합니다.

 

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)
}

 

*부연설명

닫힌 범위 연산자의 경우 모든 범위를 포함하는 연산자로 3시간마다 표시할 경우 3, 6, 9, 12를 모두 포함하기 때문에 3과 12를 모두 포함하는 범위이기 때문에 닫힌 범위 연산자를 사용했습니다.

While Loops

while loop는 조건이 거짓이 될 때까지 문장을 실행시킵니다. 이러한 종류의 루프는 첫 번째 반복이 시작되기 전에 반복 횟수를 알 수 없을 때 잘 사용됩니다. 스위프트는 2가지 종류의 while loop를 제공합니다.

 

  • while 각 루프가 통과하는 시작에서 조건식을 평가합니다. 
  • repeat-while 각 루프가 통과하는 마지막에서 조건식을 평가합니다.  

While

while loop는 1개의 조건식을 평가함으로써 시작합니다. 만약 조건식이 true이면 문장은 조건식이 false가 될 때까지 문장을 반복 실행합니다. 

 

아래는 일반적인 while loop 형식입니다.

while condition {
statements
}

 

아래 예는 뱀과 사다리라는 게임입니다. 

따라야 할 규칙으로는 :

 

  • 게임 판에는 25칸의 사각형이 있고 목표는 25칸의 사각형 도달하거나 넘는 것입니다.
  • 게임 플레이어는 보드 왼쪽 아래 모서리 "사각형 0"에서 시작합니다.
  • 각자의 차례에서 6면 주사위를 돌리고 위의 점선에 따라 지시된 수평의 경로에 따라 주사위의 숫자만큼 이동합니다.
  • 만약 당신의 차례에 사다리의 아래에 도착한다면 사다리를 타고 올라가면 됩니다.
  • 만약 당신의 차례에 뱀의 머리에 도착한다면 뱀의 꼬리로 이동하면 됩니다.

게임보드는 정수 값을 배열에 놓은 것에 해당합니다. 게임의 크기는 finalSquare라고 불리는 상수에 기반하고 배열을 초기화하고 나중에 승패를 확인하는 데 사용합니다. 플레이어는 "square zero"에서 시작하기 때문에 25가 아닌 26으로 초기화합니다.

 

let finalSquare = 25
var board = [Int](repeating: 0, count: finalSquare + 1)

 

뱀과 사다리의 경우 좀 더 구체적인 값을 설정해놓습니다. 사다리를 가진 상자는 위로 움직이기 때문에 양수를 가지고 반면에 뱀을 가진 사다리는 아래로 내려가기 때문에 음수를 가집니다.

 

board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08

 

상자 3은 사다리의 밑부분으로 상자 11까지 올라갈 수 있습니다. 이것을 표현하기 위해 board [03]은 +08과 같으며 정수 값 8과 같습니다. (11과 3의 차이) 값과 문장을 정렬하기 위해 단항 플러스 연산자(+i)와 단항 마이너스 연산자를 사용했고 10보다 낮은 수는 0을 넣었습니다 (문법 기법은 반드시 필요한 것은 아니지만 코드를 단정하게 해 줍니다)

 

var square = 0
var diceRoll = 0
while square < finalSquare {
// roll the dice
diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
// move by the rolled amount
square += diceRoll
if square < board.count {
// if we're still on the board, move up or down for a snake or a ladder
square += board[square]
}
}
print("Game over!")

 

위의 예제는 주사위를 돌리는 방법을 간단한 접근법을 사용했습니다. 무작위로 숫자를 생성하는 대신 처음 주사위 값을 0에서 시작했습니다. while loop가 돌 때마다 주사위 값은 1씩 증가시켰고 그러고 나서 주사위 값이 너무 커지는지를 확인했습니다. 반환 값이 7가 같아지면 주사위는 값은 너무 커져서 다시 1 값이 재설정됩니다. 그 결과 주사위의 연속된 값은 항상  1, 2, 3, 4, 5, 6, 1, 2...으로 진행됩니다. 

주사위를 굴린 후 플레이어는 주사위의 숫자만큼 움직입니다. 주사위를 굴려 움직여진 값이 25가 넘을 경우 게임은 끝이 납니다. 이러한 시나리오에 대처하기 위해 지나온 사각형 숫자가 전체 사각형 숫자보다 작은지 확인합니다. 사각형의 숫자가 25가 안 넘었다면 주사위를 던져 나온 값과 현재 값이 합쳐 저 플레이어는 뱀과 사다리로 아래위로 움직일 수 있습니다.   

NOTE

만약 사각형 값이 25가 넘는지 확인하지 않을 경우 런타임 오류가 발생할 수 도 있습니다. 

 

현재의 while loop가 끝난 후 루프를 다시 실행되어야 하는지 조건식을 확인합니다. 만약 플레이어가 25 숫자를 넘었다면  루프의 조건식은 거짓으로 평가되고 게임은 끝이 납니다.

위 예제의 경우 while loop를 적절히 사용한 예입니다. 왜냐하면 while loop를  시작될 때 게임의 길이가 명확하지 않기 때문입니다. 그 대신에 루프는 특정 조건식이 만족될 때까지 실행됩니다. 

Repeat - While 

repeat-while loop라고 잘 알려진 while loop의 변형은 루프의 조건식을 고려하기 전에 루프 블록을 한 번을 실행시킵니다. 그 후 조건식이 거짓이 될 때까지 계속해서 루프를 반복 실행합니다. 

 

*부연설명

while과 repeat-while의 가장 큰 차이점은 실행 블록을 한 번은 실행시킨 후 조건식이 거짓이 됨을 확인한다는 점입니다.

NOTE

스위프트에서 repeat-while loop는  다른 언어에서의 do-while 언어와 유사합니다.

여기  repeat-while loop의 일반적인 형식입니다.

 

repeat {
statements
} while condition

 

뱀과 사다리 게임을 while loop 가 아닌 repeat-while loop 다시 적용했습니다. finalSquare, board, square, diceRoll은 whileloop와 같이 똑같은 값으로 초기화했습니다.

 

let finalSquare = 25
var board = [Int](repeating: 0, count: finalSquare + 1)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
var square = 0
var diceRoll = 0

 

이번 버전 코드에서는 첫 번째로 사다리나 뱀이 있는지를 확인하는 루프를 썼습니다. 보드에서 어떤 사다리도 25로 바로 올라가는지 않기 때문에 사다리 위로 올라가서 게임을 이기는 것은 불가능합니다. 그러므로 첫 번째 루프에서 뱀인지 사다리인지 확인하는 것이 안전합니다. 게임이 시작될 때 플레이어는 "sqare zero"에서 시작하기 때문에 항상 0과 같고 효과도 없습니다. 

 

repeat {
// move up or down for a snake or ladder
square += board[square]
// roll the dice
diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
// move by the rolled amount
square += diceRoll
} while square < finalSquare
print("Game over!")

 

코드는 뱀과 사다리를 확인한 후 주사위는 굴려지고 플레이어는 주사위의 수만큼 사각형 앞으로 이동합니다. 현재 루프 실행은 끝이 납니다. 

루프의 조건식은 (while square < finalSquare) while loop와 같지만 이번에는 루프가 처음부터 끝까지 실행될 때까지 평가되지 않습니다. 이 게임에서는 전의 예였던 while loop보다 repeat-while loop 가 더 잘 맞습니다.

위의 repeat-while loop 중에서 square += board [square]는 loop의 while 조건식이 사각형이 보드 위에 있는지 확인한 후에 즉시 실행된다 이 같은 행동은 앞에서 설명했던 while loop 버전에서 배열 범위를 확인하는 필요를 제거해줍니다. 

 

*부연설명

뱀과 사다리 게임에서 while과 repeat-while의 구문의 차이는 while 구문의 경우 처음 시작에서 움직여진 값이 25가 넘을 경우 조건식은 거짓으로 판명되고 while 구문은 종료됩니다. 만약 움직여진 값이 처음부터 25가 넘는다면 단 한 번도 while구문을 실행하지 않고 끝날 수도 있습니다. 하지만 repeat while 구문의 경우 처음 시작은 나의 위치가 뱀인가 사다리인가 확인하는 것부터 시작합니다. 이는 뱀과 사다리에 있다 하더라도 마지막 숫자인 25로 갈 수 없기 때문에 설사 내가 25가 넘는 곳에 있더라도 뱀인가 사다리인가를 확인한 후 25가 넘어서 게임이 끝나는 가를 확인합니다. 이는 while구문의 경우는 조건식을 먼저 실행한 후 실행 루프를 돌려 조건식에 부합하지 않는다면 단 한 번도 실행하지 않고 바로 while구문이 종료될 수 있는 반면 repeat- while 구문의 경우 실행 루프를 먼저 두고 그 후에 조건식을 두기 때문에 설사 조건식이 false였더라도 한 번은 실행 루프를 돌리고 종료가 됩니다.