정보

다양한 제어 구조

순서도에서 마름모로 나뉘는 곳,
화살표가 되돌아가는 곳.
그것들이 코드에서는 어떤 모양인지 봅니다.

학습 목표

  1. 조건에 따라 실행할 명령이 달라지는 선택 구조를 코드로 적고, 들여쓰기가 무엇을 나타내는지 설명할 수 있다.
  2. 횟수를 아는 반복과 모르는 반복을 구별하여 알맞은 반복 방법을 고를 수 있다.
  3. 제어 구조를 겹쳐 쓴 프로그램을 읽고, 순서도로 그린 알고리즘을 코드로 옮길 수 있다.

위에서 아래로만 흐르면 할 수 있는 일이 적다

상황에 따라 다르게 굴어야 쓸모가 생깁니다.

코드는 기본적으로 맨 윗줄부터 아래로 한 줄씩 실행됩니다. 그런데 이것만으로는 만들 수 있는 프로그램이 아주 적습니다. 오늘 기온에 따라 다른 옷차림을 알려 주는 프로그램을 생각해 보세요. 기온에 따라 실행할 명령이 달라져야 합니다.

제어 구조(control structure)

프로그램이 다음에 무엇을 할지 정하는 구조입니다. 위에서 아래로 흐르는 순차, 조건에 따라 나뉘는 선택, 같은 일을 다시 하는 반복 세 가지가 있습니다.

선택 구조는 순서도에서 마름모로 그리는 것입니다. 코드에서는 if로 씁니다. 나란히 놓고 보세요.

시작 기온 확인 28도 이상? 예 아니오 반팔 안내 겉옷 안내 끝

마름모 하나가 if 한 줄이 되고, 「예」 쪽 흐름이 if 아래, 「아니오」 쪽 흐름이 else 아래에 들어갑니다. 그런데 실제로는 나뉘는 경우가 둘보다 많을 때가 흔합니다. 그럴 때 elif를 씁니다 — 「그게 아니라면 이건 어떤가」라는 뜻입니다.

temp = 12

if temp >= 28:
    print('반팔')
elif temp >= 20:
    print('얇은 겉옷')
elif temp >= 10:
    print('재킷')
else:
    print('두꺼운 외투')

기온이 12이므로 재킷이 나옵니다.


들여쓰기가 「어디까지」를 정한다

옷차림 코드에서 print가 오른쪽으로 들어가 있는 것이 보이나요? 파이썬은 이 들여쓰기로 「어디까지가 그 조건에 속한 명령인지」를 나타냅니다. 조건이 참일 때 할 일이 세 줄이면 세 줄 모두 들여씁니다. 다시 왼쪽으로 나오면 조건과 상관없이 늘 하는 일이 됩니다.

보기 좋으라고 넣는 것이 아닙니다

글을 쓸 때 문단 첫 줄을 들여쓰는 것은 보기 좋으라고 하는 일이지만, 파이썬에서 들여쓰기는 뜻을 바꿉니다. 들여쓰기를 빠뜨려 조건에 속해야 할 줄이 밖으로 나가면 조건과 상관없이 늘 실행됩니다. 공백 몇 개만 어긋나면 오류가 나서 알아챌 수 있지만, 한 단계 통째로 밖으로 나오면 오류가 나지 않아 더 곤란합니다. 순서도에서 화살표를 잘못 그은 것과 같은 잘못입니다.

elif는 위에서부터 차례로 본다

맞는 조건을 만나면 거기서 멈추고 아래는 보지 않습니다. 그래서 순서를 바꾸면 답이 달라집니다. 옷차림 코드에서 temp >= 10을 맨 위로 올리면 기온이 30도일 때도 10 이상이라는 조건에 먼저 걸려 「재킷」이 나옵니다. 이 코드처럼 「이상」으로 범위를 나눌 때는 좁은 조건부터 위에 두어야 합니다.

스스로 확인

옷차림 코드에서 기온이 28이면 무엇이 나올까요? 9이면 무엇이 나올까요? 경계에 걸리는 값을 넣어 확인해 보세요.

같은 줄을 백 번 적을 수는 없다

몇 번인지 아느냐 모르느냐에 따라 쓰는 것이 다릅니다.

리스트에 저장된 점수를 하나씩 꺼내 화면에 출력한다고 해 봅시다. 인덱스를 하나하나 적으면 print를 다섯 줄 써야 하고, 값이 삼백 개면 삼백 줄을 써야 합니다. 이럴 때 쓰는 것이 반복입니다. 순서도에서 화살표가 위로 되돌아가는 곳입니다.

몇 번인지 알 때 — for

for는 정해진 것들을 앞에서부터 하나씩 꺼내며 반복합니다. 리스트를 그대로 넘겨도 되고, range로 횟수를 정해 줄 수도 있습니다.

scores = [88, 74, 95, 61, 79]

for score in scores:
    print(score)

for day in range(1, 8):
    print(day, '일째')

리스트를 넘긴 반복은 점수 다섯 개를 차례로 출력하고, range를 쓴 반복은 1일째부터 7일째까지를 출력합니다. range(1, 8)이 8이 아니라 7까지인 것에 주의하세요. 「1에서 시작해 8 앞에서 멈춘다」고 읽으면 맞습니다. 리스트를 잘라 낼 때와 같은 규칙입니다.

range를 쓰는 세 가지 방식
적는 법 나오는 수 언제 쓰나
range(5) 0, 1, 2, 3, 4 다섯 번 반복할 때. 인덱스와 짝이 맞습니다.
range(1, 8) 1, 2, 3, 4, 5, 6, 7 1부터 세고 싶을 때. 날짜나 층수처럼 0이 어색한 것들.
range(0, 10, 2) 0, 2, 4, 6, 8 건너뛰며 셀 때. 마지막 수가 뛰는 간격입니다.

몇 번인지 모를 때 — while

몇 번 해야 하는지 미리 알 수 없는 일도 있습니다. 「하루 8000걸음씩 걸어 목표 3만 걸음을 채우려면 며칠이 걸리는가」 같은 물음입니다. 이럴 때는 조건이 참인 동안 계속 하는 while을 씁니다.

total = 0
days = 0

while total < 30000:
    total = total + 8000
    days = days + 1

print(days, '일이 걸립니다.')

4일이 나옵니다. 우리는 코드를 쓸 때 4라는 수를 몰랐습니다. 조건을 적어 두었더니 컴퓨터가 반복할 때마다 조건을 확인해 네 번 반복하고 멈춘 것입니다. 이것이 while을 쓰는 이유입니다.

조건을 바꿔 주는 줄이 없으면 끝나지 않습니다

걸음 수 코드에서 total = total + 8000을 빼면 어떻게 될까요? total이 계속 0이므로 조건이 영원히 참이고, 프로그램은 끝나지 않고 계속 반복합니다. 이것을 무한 반복이라고 합니다. 알고리즘이 갖추어야 할 유한성, 곧 「반드시 끝나야 한다」는 조건을 어긴 것입니다. while을 쓸 때는 무엇이 조건을 거짓으로 바꿔 주는지를 반드시 확인하세요.


찾았으면 멈춘다 — break

책 다섯 권이 42, 17, 63, 8, 35 순으로 꽂힌 책장에서 번호 8인 책을 찾는다고 해 봅시다. 찾은 뒤에도 끝까지 순회하는 것은 헛일입니다. 반복을 즉시 끝내는 명령이 break입니다.

books = [42, 17, 63, 8, 35]

for i in range(len(books)):
    if books[i] == 8:
        print('인덱스', i, '위치에 있습니다.')
        break

「인덱스 3 위치에 있습니다.」가 나오고 반복이 끝납니다. break가 없으면 인덱스 4까지 헛되이 확인하게 됩니다. 맨 앞부터 하나씩 확인하다가 찾으면 멈추는 것, 이것이 순차 탐색이 코드가 된 모습입니다.

스스로 확인

다음 둘은 for와 while 중 어느 쪽이 어울릴까요? 「명단 30명에게 문자를 보낸다」와 「맞힐 때까지 숫자를 다시 묻는다」.

구조 안에 구조를 넣는다

표를 순회하려면 반복이 두 겹 필요합니다.

지금까지 본 것은 제어 구조 하나씩이었습니다. 그런데 실제 프로그램은 구조 안에 구조를 넣어 씁니다. 이것을 중첩 제어 구조라고 합니다.

가장 흔한 것이 반복 안의 반복입니다. 가로세로로 놓인 표를 순회하려면 행을 하나씩 꺼내고, 그 행 안에서 요소를 하나씩 꺼내야 하기 때문입니다. 아래는 밝기 값이 저장된 3행 5열짜리 표에서 어두운 점(값이 0인 요소)이 몇 개인지 세는 코드입니다.

image = [
    [255, 255, 0, 255, 255],
    [255, 0, 0, 0, 255],
    [0, 0, 0, 0, 0],
]

dark = 0

for row in image:
    for value in row:
        if value == 0:
            dark = dark + 1

print(dark)

결과는 9입니다. 안쪽을 살펴보면 구조가 세 겹입니다.

중첩된 세 겹의 제어 구조
깊이 무엇인가 하는 일
바깥 반복 for row in image 행을 하나씩 꺼냅니다. 세 번 반복합니다.
가운데 반복 for value in row 그 행 안의 요소를 하나씩 꺼냅니다. 행마다 다섯 번 반복합니다.
안쪽 선택 if value == 0 값이 0일 때만 dark를 1 늘립니다.

안쪽은 몇 번 반복할까요? 바깥이 세 번 반복하는 동안 한 번마다 안쪽이 다섯 번 반복하니 3 × 5 = 15번입니다. 표의 요소 수와 정확히 같습니다. 중첩된 반복의 횟수는 곱해집니다. 바깥이 백 번, 안쪽이 백 번이면 만 번이 됩니다. 깊이가 한 겹 늘 때마다 일이 급하게 불어날 수 있다는 뜻입니다.

깊이는 들여쓰기로 읽습니다

중첩이 깊어질수록 들여쓰기가 한 단계씩 더 들어갑니다. 코드를 읽을 때 왼쪽 끝이 어디까지 들어가 있는지만 봐도 지금 몇 겹 안에 있는지 알 수 있습니다. 거꾸로 말하면, 들여쓰기가 어긋나면 몇 겹인지가 달라져 프로그램이 전혀 다른 일을 하게 됩니다.

스스로 확인

어두운 점을 세는 코드에서 if 줄을 지우고 그 아래 줄의 들여쓰기를 한 단계 줄이면 어떤 값이 나올까요? 지금까지 센 것이 무엇이었는지 생각하며 답해 보세요.

손으로 하는 정렬이 여섯 줄이 된다

제어 구조를 배운 이유가 여기서 드러납니다.

책 다섯 권이 42, 17, 63, 8, 35 순으로 꽂혀 있습니다. 이것을 번호 순으로 다시 꽂는 방법 하나는 이렇습니다. 바로 옆에 놓인 두 권의 번호를 비교하고, 앞의 것이 더 크면 맞바꿉니다. 그리고 한 권 오른쪽으로 옮겨 같은 일을 반복합니다. 끝까지 가면 가장 큰 번호가 맨 뒤에 도착하므로, 다음 회전에서는 맨 뒤 한 권을 뺀 나머지로 다시 합니다.

손으로 하면 종이에 화살표를 그려 가며 한참 걸립니다. 제어 구조로 적으면 이렇게 됩니다.

books = [42, 17, 63, 8, 35]

for turn in range(4):
    for i in range(4 - turn):
        if books[i] > books[i + 1]:
            books[i], books[i + 1] = books[i + 1], books[i]

print(books)

결과는 [8, 17, 35, 42, 63]입니다. 한 줄씩 읽어 보면 두 겹의 for 반복, if 선택, 두 요소를 맞바꾸는 줄뿐입니다.

정렬 코드의 각 줄이 무엇에 해당하는가
코드 제어 구조 무엇을 하는가
for turn in range(4) 반복 회전을 네 번 합니다. 회전마다 맨 뒤 요소 하나가 확정됩니다.
for i in range(4 - turn) 반복 그 회전에서 이웃끼리 비교할 인덱스를 순회합니다. 회전이 갈수록 짧아집니다.
if books[i] > books[i + 1] 선택 앞이 더 크면 맞바꿉니다. 아니면 그대로 둡니다.

books[i], books[i + 1] = books[i + 1], books[i]는 두 요소의 값을 한 번에 맞바꾸는 파이썬의 방식입니다. 한쪽을 먼저 덮어쓰면 그 값이 사라져 버리므로, 보통은 값을 잠시 저장해 둘 변수를 하나 더 두어야 합니다. 파이썬은 그 번거로움을 한 줄로 줄여 줍니다.

비교는 몇 번 일어날까

안쪽 반복이 회전마다 4번, 3번, 2번, 1번 반복하므로 모두 10번입니다. 같은 방법으로 다섯 권을 손으로 정렬해도 비교가 꼭 열 번 일어납니다. 손으로 세던 것과 코드가 세는 것이 정확히 같습니다. 달라진 것은 사람이 하느냐 컴퓨터가 하느냐뿐입니다.


그래서 제어 구조를 배운다

순서도로 그린 알고리즘은 사람이 읽으라고 그린 그림이었습니다. 제어 구조를 알고 나면 그 그림을 컴퓨터가 실행할 수 있는 형태로 옮길 수 있습니다. 마름모는 if가 되고, 되돌아가는 화살표는 for나 while이 됩니다. 문법을 외우는 것이 목적이 아니라, 머릿속에서 짠 절차를 실제로 실행되게 만드는 것이 목적입니다.

스스로 확인

책이 다섯 권이 아니라 여섯 권이라면 코드에서 어느 숫자를 바꿔야 할까요? 그 숫자를 값을 세지 않고도 알아내게 하려면 무엇을 쓰면 될까요?

핵심 정리

선택 구조

if · elif · else

  • 순서도의 마름모가 if 한 줄이 된다
  • elif는 위에서부터 차례로 보고 맞으면 멈춘다
  • 「이상」으로 범위를 나눌 때는 좁은 조건부터 위에

반복 구조

for · while

  • 몇 번인지 알면 for, 모르면 while
  • range는 끝 수 앞에서 멈춘다
  • 조건을 바꿔 주는 줄이 없으면 무한 반복에 빠진다

들여쓰기와 중첩

어디까지가 조건에 속하는가

  • 들여쓰기는 보기 좋으라고가 아니라 뜻을 정한다
  • 표를 순회하려면 반복이 두 겹 필요하다
  • 중첩된 반복의 횟수는 곱해진다

알고리즘이 코드가 된다

배우는 이유

  • 마름모는 if, 되돌아가는 화살표는 반복이 된다
  • 손으로 세던 비교 횟수가 코드에서도 그대로다
  • 목적은 문법이 아니라 짠 절차를 실제로 실행되게 하는 것

확인 퀴즈

배운 내용을 확인해 봅시다.

Q1

for day in range(1, 8)에서 day에 들어가는 마지막 수는?

Q2

while로 만든 반복이 끝나지 않는 가장 흔한 까닭은?

Q3

바깥 반복의 횟수가 3번, 그 안의 반복의 횟수가 5번이라면 안쪽 명령은 몇 번 실행될까?