6.2.1 반복 작업은 컴퓨터에게

매일 아침 7시에 알람 울리기, 쇼핑몰에 사고 싶은 물건이 들어오는지 지켜보기, 책 1만 번 베껴 쓰기, 숫자를 세다가 3, 6, 9가 나오면 박수 치기. 이 일들은 어떤 똑같은 행위를 규칙에 따라 여러 차례 되풀이한다는 공통점이 있다. 이런 일을 반복 작업이라고 한다. 사람은 반복 작업을 싫어한다. 똑같은 일을 정확하게 반복하는 것은 힘들고, 지치고, 따분하고, 실수하기 쉽기 때문이다.

컴퓨터는 동일한 작업을 무한히 반복하는 데 특화되어 있다. 프로그램을 만들어 반복 작업을 지시하면, 컴퓨터가 자동으로 일을 해 줄 것이다. 문제는, 프로그램에서 어떻게 반복을 지시할 것인가다.

반복 작업을 프로그래밍하는 방법

1부터 4까지 숫자를 차례대로 화면에 출력하는, 가장 간단한 반복을 프로그램으로 구현해 보자.

코드 6-16 1부터 4까지 출력하는 프로그램 1

print(1)
print(2)
print(3)
print(4)

위의 프로그램은 가장 원시적인 방법으로 반복 작업을 지시한 예다. 실행 결과만 보면 이 프로그램은 제 몫을 해 낸다. 하지만 프로그래밍 효율성과 확장성의 측면에서는 문제가 있다.

첫 번째 문제는 수를 증가시키는 과정이 아니라 (프로그래머에 의해) 계산된 결과가 프로그램에 포함되었다는 것이다. 이 프로그램은 계산해야 할 내용이 바뀔 때마다 프로그래머가 출력할 값을 계산해야 한다. 계산해야할 내용이 더 복잡한 계산이어도 이 방법이 효과적일까? 반복해야 하는 횟수가 훨씬 더 많다면 다 계산할 수 있을까?

두 번째 문제는 이 프로그램이 작업을 반복하라고 지시하는 프로그램이이 아니라, 단순히 작업을 여러 번 지시하는 프로그램이라는 것이다. 사람의 말로 하면, “이걸 네 번 하세요.”가 아니라 “이걸 하세요. 이걸 하세요. 이걸 하세요. 이걸 하세요.”라고 말한 셈이다. 이런 방식은 반복할 횟수가 한두 번에 불과할 때는 괜찮을 수 있지만 반복할 횟수가 많거나 그 때 그 때 바뀌는 경우에는 사용할 수 없다.

이어지는 내용에서 첫 번째 문제를 해결하는 방법을 먼저 생각해보고, 프로그래밍 언어의 반복 표현을 사용해 두 번째 문제도 해결해 보자.

6.2.2 반복의 한 주기를 온전히 나타내기

반복 작업을 프로그래밍할 때는 전체 반복을 생각하기 전에 반복의 한 주기에서 일어나는 일이 무엇인지부터 잘 생각해 보는 것이 좋다. 한 주기를 정확하게 수행해 낼 수 있다면 몇 번을 반복해야 하는지는 큰 문제가 아니다.

코드 6-17 코드 6-16의 한 주기

print(1)

위 코드는 코드 6-16의 전체 반복에서 한 주기만 떼어낸 것이다. 이 코드를 보고 한 주기에서 일어나는 일이 무엇인지 잘 생각해 보자. 화면에 수를 출력하는 print() 함수가 있다. 그것 뿐일까? 출력해야 할 수, 1도 있다. 이 수는 어떻게 구한 것일까? 코드에는 나와 있지 않지만, 프로그래머가 직접 수행한 일이 있다. 수를 이전 주기에서 1 만큼 증가시킨 것이다. 그러므로 이 경우 한 주기에서 실제로 수정한 일은 두 가지다. 수를 1 증가시키는 것과, 그 수를 화면에 출력하는 것.

프로그래머가 가로챈 작업을 코드에 추가하여 반복의 한 주기를 온전하게 나타내면 다음과 같다.

코드 6-18 온전히 표현한 한 단계의 작업

number += 1     # 수를 1 증가시킨다
print(number)   # 수를 출력한다

프로그래머가 대신했던 일을 컴퓨터에게 알려줌으로써, 한 행이었던 코드가 두 행으로 늘어났다. 코드가 늘어난 것은 단점이지만, 그 대신 프로그래머가 직접 수를 계산하는 수고가 줄어들었다. 이로써 반복 횟수가 많아지더라도 프로그래머가 고생할 일은 없다.

다음은 각 반복 주기를 온전하게 나타내도록 프로그램을 수정한 것이다.

코드 6-19 1부터 4까지 출력하는 프로그램 2

number = 0      # 수를 세기 위해 변수가 필요하다

number += 1     # 수를 1 증가시킨다
print(number)   # 수를 출력한다
number += 1
print(number)
number += 1
print(number)
number += 1
print(number)

이제 프로그램에 직접 1, 2, 3, …을 직접 계산해 넣지 않아도 된다. 이로써 코드 6-16에서 지적한 두 문제점 중 하나를 해결했다. 하지만 똑같은 코드를 여러 번 반복하여 입력해야 하는 문제는 여전히 남아 있다. 한 주기에 해당되는 코드인 number += 1print(numer) 만 컴퓨터에게 가르쳐주고, ‘이걸 네 번 실행하시오’라고 지시할 수 있다면 편리할 것이다. 이 지시는 컴퓨터가 동일한 코드를 여러 번 실행하도록 하는 명령 while 문으로 나타낼 수 있다.

6.2.3 while 문: 일반적인 반복

while 문은 지정된 조건이 유지되는 동안 코드를 계속 반복하는 명령이다. while 문은 기능이 단순하다. 그 때문에 사용하기가 다소 불편할 때도 있지만 다양한 형태의 반복을 나타낼 수 있다는 장점도 있다.

while 문을 작성하는 양식은 다음과 같다.

while 조건:
    본문 (반복 실행할 코드)

while 문의 양식은 if 문과 형태가 거의 똑같다. while 문은 if 문처럼 지정된 조건이 참일 때만 본문을 실행한다. while 문과 if 문의 차이는 본문의 코드가 실행되는 횟수다. if 문은 본문을 최대 한 번만 실행하지만, while 문은 조건이 참으로 유지되는 동안에는 몇 번이든 조건 검사와 코드 실행을 반복한다. 영어 단어 ‘while’은 “… 인 동안”이라는 뜻이고, while 문을 우리 말로 옮기면 “조건이 유지되는 동안 본문의 코드를 반복 실행하라”라는 뜻으로 풀이할 수 있다.

반복 횟수 관리하기

while 문에는 반복이 유지될 조건만을 지정할 수 있다. 반복을 어떻게 유지하고 어떻게 끝낼지는 프로그래머가 직접 설계해야 한다. 코드를 ‘N번’ 반복하도록 하려면 코드가 몇 번 반복되었는지를 변수를 이용해 기억하고 검사해야 한다. 다음은 변수를 이용해 반복 횟수를 기억하며 '안녕'을 세 번 출력하는 프로그램이다. 파이참에서 while1.py 파일을 새로 만들고, 다음 코드를 입력해 실행해 보아라.

코드 6-20 while 문의 사용예 (while1.py)

i = 0              # 현재 반복된 횟수를 기억하기 위한 변수
while i < 3:       # 조건(i < 3)이 유지되는 동안 본문을 반복 실행하라
    print('안녕')  # 화면에 메시지 출력 (반복을 통해 수행하려는 일)
    i += 1         # 반복된 횟수가 한 번 늘어났음을 기억

while 문을 다룰 때 반복 흐름을 잘못 설계하거나 조건을 틀리게 입력하면 코드가 무한히 반복 실행될 수 있다. 이번 절의 코드를 따라 입력하다가 오타가 나서 이런 상황이 생긴다면, 당황하지 말고 ‘Ctrl + C’(실행 중지) 키 조합을 입력해 프로그램의 실행을 중지하면 된다.

화면에 ‘안녕’이 세 번 출력되었으면 첫 while 문을 성공적으로 작성한 것이다. while 문의 동작을 제대로 이해하려면 반복 과정을 한 단계씩 추적해보는 연습이 필요하다. 아래에서 설명하는 내용은 코드 6-20이 실행되는 과정을 한 단계씩 풀어 설명한 것이다. 풀이가 좀 길지만 어려운 내용은 아니다. 코드와 비교해가며 차근차근 읽어보도록 하자.

  1. while 문을 시작하기 전, while 문이 반복 실행된 횟수를 기억하기 위해 변수 i를 정의하였다. i라는 이름은 반복 횟수를 가리키는 변수의 관례적인 이름이다. 이 변수에는 처음에 0을 대입했다. 아직 while 문이 실행된 적이 없기 때문이다.
  2. while 문을 최초로 실행할 차례가 되면 컴퓨터는 while 문의 조건을 검사한다. 이 시점에서 i는 0이고 0은 5보다 작다. 따라서 조건은 참이다. 그러므로 while 문의 본문을 실행한다.
  3. 본문에는 반복을 통해 수행하려는 일(화면에 메시지 출력)이 포함되어 있다. 그리고 수행할 일과는 관련 없지만 반복을 제어하기 위한 코드(현재 반복된 횟수를 1 증가)도 포함되어 있다. 컴퓨터가 본문을 실행하여 화면에 ‘안녕’이 출력되고 i는 0에서 1 증가하여 1이 된다.
  4. 컴퓨터는 본문의 실행을 마친 후 조건이 여전히 참인지 한 번 더 검사한다. i가 1로 변경되었지만 여전히 5보다 작으므로 조건이 참이다. 본문을 한 번 더 실행하여 화면에 ‘안녕’이 한 번 더 출력되고 i는 2가 된다.
  5. 다시 조건을 검사한다. i는 2로 여전히 3보다 작다. 본문을 한 번 더 실행한다. 화면에 ‘안녕’이 세 번째로 출력되고 i는 3이 된다.
  6. 다시 조건을 검사한다. 이 순간 i는 3이어서 3보다 작지 않다. 조건은 거짓이다. 이번에는 while 문의 본문을 실행하지 않는다. 다음 차례의 명령으로 넘어간다.

while 문을 사용할 때는 반복을 통해 수행하려는 작업만큼이나 반복을 올바르게 설계하는 것도 중요하다. 반복을 잘못 설계하면 원하는 원하는 결과를 얻을 수 없다. 조건을 i > 3으로 잘못 지정했다면 ‘안녕’은 한 번도 출력되지 않았을 것이다. 매 주기에서 반복된 횟수를 갱신하지 않았다면 ‘안녕’이 무한히 반복 출력되었을 것이다.

반복된 횟수를 관리함으로써 반복을 제어하는 방법은 while 문을 사용하는 가장 흔한 패턴이다. 코드를 n회 반복 실행하는 방법을 다음과 같이 정리하여 두자.

  1. 반복을 시작하기 전, 반복 횟수를 기억할 변수(주로 i)에 0을 대입한다.
  2. 반복 유지 조건을 i < n으로 지정한다.
  3. while 문의 본문 코드 블록 안에서 필요에 따라 i의 값을 활용한다.
  4. while 문의 본문 코드 블록 안에서 i의 값을 1 증가시킨다.

컬렉션 순회하기

반복은 컬렉션을 순회하는 데도 사용된다. 순회란 컬렉션을 순서대로 돌며 내용을 읽거나 조작하는 것이다. 다음은 무지개 색을 표현한 리스트의 전체 내용을 화면에 출력하는 프로그램이다. 파이참에서 while2.py 파일을 새로 만들고, 다음 코드를 입력해로 실행해 보아라.

코드 6-21 무지개 색 리스트의 내용을 순회하며 출력 (while2.py)

rainbow = ['Red', 'Orange', 'Yellow', 'Green', 'Blue', 'Indigo', 'Violet']
index = 0                    # 몇 번째 원소를 읽을 차례인지 기억하는 변수

while index < len(rainbow):  # index 변수가 리스트의 길이보다 작은 동안 반복
    print(rainbow[index])    # rainbow 리스트의 index 번째 원소를 화면에 출력
    index += 1               # 다음 원소를 가리키도록 index 값을 1 증가

실행 결과:

Red
Orange
Yellow
Green
Blue
Indigo
Violet

코드 6-21은 앞에서 배운 while 문 사용 패턴을 벗어나지 않는다. 하나씩 따져보자.

  1. 컬렉션의 몇 번째 원소를 읽어야 하는지 기억하는 변수 index가 있다. 이 변수에는 반복을 시작하기 전에 0을 대입했다. 따라서 index는 처음에 컬렉션의 첫 번째 원소를 가리키게 된다.
  2. 반복 유지 조건은 index가 컬렉션의 범위를 벗어나지 않은 동안이다. 즉, 컬렉션의 길이(len(rainbow))보다 index가 작지 않을 때이다.
  3. while 문의 본문 코드 블록 안에서 indexrainbow[index]]와 같이 컬렉션의 index번째 원소를 가리키는 데 할용된다.
  4. while 문의 본문 코드 블록 안에서 index의 크기는 1씩 증가한다. 따라서 반복이 1회 진행될 때마다 컬렉션의 다음 원소를 읽을 수 있다.

이와 같이 다음 번에 읽을 위치를 갱신하며 컬렉션을 순회하는 반복 패턴은 컬렉션과 함께 자주 사용된다. 이 패턴을 매번 작성하려면 귀찮으므로, 파이썬에는 컬렉션 순회를 위한 전용 명령도 있다. 6.2.3 for 문에서 더 알아 볼 것이다.

흐름이 불규칙한 반복

수를 차례대로 세거나 컬렉션을 순서대로 읽는 것은 규칙에 따라 일정하게 흐르는 반복 작업이다. 시작값, 종료값, 한 주기의 변화량을 알면 실행 결과를 예측할 수도 있다. 그런데 반복 작업 중에는 규칙이 일정하지 않고 흐름을 예측하기 어려운 것도 있다. 다음 프로그램에서 while 문의 본문이 몇 번 실행될지 한 번 예상해 보자. 예상해 봤으면 파이참에서 while3.py 파일을 만들어 코드를 입력하고 실행해보기 바란다.

코드 6-22 그만을 입력할 때까지 (while3.py)

text = '아무 메시지나 입력하세요. 그만하려면 \'그만\'을 입력하세요.'
while text != '그만':
    print('컴퓨터: ' + text)
    text = input()

실행 결과:

컴퓨터: 아무 메시지나 입력하세요. 그만하려면 '그만'을 입력하세요.
안녕?
컴퓨터: 안녕?
따라하지마
컴퓨터: 따라하지마
그만

코드 6-22는 사용자가 입력한 텍스트가 ‘그만’이 아닌 동안(즉, 사용자가 ‘그만’을 입력할 때까지) 사용자에게서 입력받은 메시지를 계속 출력한다. 이 while 문은 실행을 마치기 전까지는 본문이 몇 번 반복될지 알 수 없다. 어떤 ‘횟수’만큼이 아니라 ‘돌아가는 상황’에 따라 얼마나 코드가 반복 실행될지가 결정되는 것이다. while 문은 반복이 유지될 조건만으로 반복을 수행하기 때문에 반복 흐름에 일정한 규칙이 없을 때도 잘 작동한다. while 문에는 다음과 같은 반복 조건이 모두 사용될 수 있다.

  • 사용자가 엔터 키를 누를 때까지
  • 2199년 12월 31일이 되기 전까지
  • 읽을 파일 내용이 남아있는 동안
  • 서버에서 응답이 올 때까지
  • 보름달이 뜰 때까지
  • 열차가 부산에 도착하기 전까지

무한 반복

while 문의 조건을 잘못 지정하면 무한 반복이 발생할 수 있어 조심해야 한다. 그런데 오류가 아니라 프로그래머가 의도적으로 무한 반복을 발생시키는 경우도 있다. 무한 반복을 의도적으로 발생시킬 때는 대개 while 문의 조건에 True를 명시한다. True는 언제나 참이므로, 코드를 항상 반복하겠다는 의도가 명확하게 드러난다.

코드 6-23 명시적인 무한 반복

while True:
    print('이 메시지는 무한히 반복 출력됩니다.')

무한 반복이 활용되는 곳은 꽤 많다. 몇 가지만 예를 들어 보자.

  • 사용자와 계속 상호작용하는 프로그램 (우리가 일상에서 사용하는 수많은 프로그램)
  • 웹 문서를 지속적으로 탐색하며 정보를 수집하는 크롤링 로봇
  • 음악을 무한 반복 재생하는 프로그램
  • 컴퓨터가 켜졌을 때부터 꺼질 때까지 시스템을 관리하는 운영 체제

그런데 이런 프로그램들도 언젠가는 무한 반복을 중지해야 할 때가 있을 것이다. 사용자가 프로그램 종료 버튼을 눌렀다든지, 더이상 탐색할 문서가 없다든지, 음악 파일이 삭제되어 음악을 재생할 수 없다든지. 언젠가는 이런 예외 상황이 발생할 것이고, 그 때는 반복을 중지해야 한다. 반복을 임의로 중지하는 방법은 6.2.5 continue 문과 break 문에서 설명한다.

while 문을 사용해야 할 때

파이썬의 반복 명령에는 while 문 외에도 for 문이 있다. while 문은 일반적인 반복 명령을 모두 나타낼 수 있어, for 문으로 나타내기 어려운 반복 작업도 프로그래밍할 수 있다. 특히 과정이 불규칙한 반복이나 무한 반복을 나타낼 때는 for 문보다는 while 문으로 나타내는 것이 적절하다.

6.2.4 for 문: 컬렉션 순회

for 문은 while 문과 마찬가지로 코드를 반복 실행하는 명령이다. while 문은 여러 목적에 활용할 수 있는 일반적인 반복 기능을 제공하고, for 문은 컬렉션 순회에 특화된 반복 기능을 제공한다. for 문은 while 문보다는 나타낼 수 있는 반복 작업의 종류가 적지만, while 문보다 간결한 표현으로 컬렉션을 순회할 수 있다는 장점이 있다. 많은 데이터를 다뤄야 할 때는 while 문보다 for 문이 활용성이 더 높다.

먼저 for 문을 작성하는 양식을 살펴보자.

for 변수 in 컬렉션:
    본문 (반복 실행할 코드)

그동안 def 문, if 문, while 문을 배웠으므로 이제 헤더 행과 본문으로 구성된 표현이 낯설지 않을 것이다. 항상 그렇듯이 헤더 행은 콜론(:)으로 끝낸다. 본문은 공백 4개씩 들여쓰기해야 하며, 본문에는 원하는 만큼 여러 행의 코드를 입력할 수 있다.

for 문에는 while 문보다는 입력할 내용이 많다. 헤더 행을 자세히 보자. in 예약어 뒤에는 반복을 통해 순회하려는 컬렉션을 지정한다. for 문은 지정된 컬렉션의 길이만큼 본문의 코드를 반복 실행한다. 예를 들어 컬렉션에 원소가 50개 들어있다면 본문은 50회 실행된다. 헤더 행의 변수는 반복이 진행되는 동안 컬렉션의 원소를 한 주기에 하나씩 대입받을 변수다. 본문에서 이 변수를 이용해 컬렉션의 원소를 사용할 수 있다.

for 문의 실제 사용 예를 보면 이해하기 쉬울 것이다. 다음 프로그램은 while 문으로 작성했던 무지개색 순회 프로그램을 for 문을 사용하는 버전으로 수정한 것이다. 파이참에서 for1.py 파일을 만들고, 다음 코드를 입력하여 실행해 보자.

코드 6-24 무지개 색 리스트의 내용을 순회하며 출력 (for1.py)

rainbow = ['Red', 'Orange', 'Yellow', 'Green', 'Blue', 'Indigo', 'Violet']

for color in rainbow:
    print(color)

위 코드의 실행 결과는 while 문을 사용한 코드 6-21과 동일하다. for 문은 rainbow의 길이 만큼 본문 코드를 반복 실행한다. 그리고 각 반복 주기에서 rainbow의 원소를 하나씩 꺼내 color 변수에 대입한다. 본문에서 color를 화면에 출력하였으므로, rainbow의 원소가 화면에 하나씩 7개 모두 출력된다. for 문을 사용함으로써 while 문을 사용했을 때와 동일한 결과를 더 적은 코드로 얻을 수 있었다.

한 가지 예를 더 들어보자. 다음은 시퀀스에 들어 있는 모든 수의 합계를 계산하는 함수(sum() 함수를 흉내낸 것)를 for 문을 이용해 정의한 것이다.

코드 6-25 시퀀스의 모든 수의 합을 계산하는 함수 (my_sum.py)

def my_sum(numbers):
    """numbers 의 모든 원소의 합을 반환한다."""
    total = 0  # 합계
    for n in numbers:
        total += n
    return total

print(my_sum([1, 2, 3, 4, 5]))

for 문과 range() 함수

수열을 나타내는 함수 range()는 for 문과 함께 활용하기에 좋다. 이 함수가 필요한 상황을 알아보기 위해 간단한 숫자 계산 프로그램을 예로 들어 보자. 다음은 for 문을 이용해 1부터 10까지의 모든 짝수의 합을 계산하는 프로그램이다.

코드 6-26 1부터 10까지의 모든 짝수의 합 (for2.py)

total = 0  # 합계
for n in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:
    if n % 2 == 0:  # n 이 짝수인 경우,
        total += n  # total에 n을 더함
print(total)

위 코드는 for 문을 이용해 1 부터 10까지의 자연수를 담은 리스트([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])를 순회하며, 본문에서 if 문을 이용해 수가 짝수인 경우에만 합산하도록 했다. 지금까지 학습한 독자들이라면 충분히 이해할 수 있을 것이다. 이제 합할 수의 범위를 바꿔 보자. 더해야 할 수의 범위가 달라지더라도 for 문에 지정한 리스트만 변경하면 된다. for 문에 넣을 1부터 1천만까지의 모든 자연수의 리스트를 나타내 보자.

코드 6-27 1부터 1천만까지의 모든 자연수…?

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ..., 10000000]

리스트를 작성하다 보니 이 방법에 문제가 있다는 걸 알았다. 1부터 1천만까지의 리스트를 코드로 작성하기가 너무 힘들다. 시간과 노력을 충분히 들인다면 할 수는 있곘지만, 인생을 덜 낭비하는 방법은 없을까?

range() 함수는 수의 범위를 지정하는 것만으로 수열을 생성할 수 있어, 광범위한 범위의 리스트 대신 사용하기에 좋다. 1부터 1천만까지의 모든 자연수는 range(1, 10000001)로 간단히 나타낼 수 있다. 타이핑할 양이 천만 분의 일쯤으로 줄어든 것 같다. 이 방법으로 다시 문제를 해결해 보자.

코드 6-28 1부터 1천만까지의 모든 짝수의 합 (for3.py)

total = 0  # 합계
for n in range(1, 10000001):
    if n % 2 == 0:  # n 이 짝수인 경우,
        total += n  # total에 n을 더함
print(total)

range() 함수는 세 번째 매개변수를 통해 수열의 간격을 지정할 수 있다. 이를 이용하면 ‘1부터 1천만까지의 모든 짝수’를 range() 함수로 바로 표현할 수도 있다.

코드 6-29 1부터 1천만까지의 모든 짝수의 합 (for4.py)

total = 0  # 합계
for n in range(2, 10000001, 2):
    total += n
print(total)

위 코드에서는 range() 함수가 짝수의 수열을 생성하므로, 각 원소가 짝수인지 검사하는 if 문이 없어도 된다. 이처럼 다양한 규칙의 등차수열을 순회할 때 for 문과 range()를 함께 사용하면 편리하다.

코드를 N회 반복 실행하기

for 문을 반드시 컬렉션 순회에만 사용해야 하는 것은 아니다. for 문과 range() 함수 조합은 단순히 본문의 코드를 n번 반복 실행하고 싶을 때도 많이 활용된다. 다음 예를 살펴보자.

코드 6-30 화면에 ‘안녕?’을 n번 출력하기 (for5.py)

print('몇 번 반복하시겠습니까?')
n = int(input())    # 반복할 횟수
for _ in range(n):  # 코드를 n번 반복 실행
    print('안녕?')

실행 결과:

몇 번 반복하시겠습니까?
3
안녕?
안녕?
안녕?

코드 6-30은 사용자가 원하는 횟수만큼 for 문의 본문을 실행한다. 이 프로그램의 for 문에서는 range()의 크기(본문을 몇 번 반복할지)만이 중요하다. 컬렉션의 각 원소는 본문에서 사용하지 않으므로, 원소를 대입받을 변수의 이름을 _로 하였다. 파이썬 문화에서 이 변수 이름은 변수에 대입된 값을 사용하지 않는다는 의미다.

for 문을 사용해야 할 때

for 문은 컬렉션을 순회하는 과정을 자동으로 처리한다. range() 함수를 함께 사용하면 간단한 규칙에 따라 반복하는 과정을 나타내기에도 좋다. for 문으로 할 수 있는 일은 while 문으로도 할 수 있지만 그럴 경우 반복 과정을 프로그래머가 직접 제어해야 해서 코드가 길고 복잡해진다. 수행하려는 반복 작업의 내용을 잘 생각해보고 for 문을 사용해 나타낼 수 있는 반복 작업이라면 for 문으로 작성하도록 하자.

6.2.5 continue 문과 break 문

3장에서 배운 return 문을 기억할 것이다. return 문을 사용하면 함수의 실행이 다 끝나지 않아도 도중에 중지시킬 수 있었다. for 문과 while 문에도 코드 본문의 실행을 도중에 중지시키는 기능이 있다. continue 문과 break 문이 그것이다.

continue 문: 이번 주기의 실행을 중지

continue 문은 반복 도중에 한 주기의 실행을 중지하고 다음 주기로 넘어도록 하는 명령이다. continue 문을 사용하려면 for 문의 본문 안에서 다음 주기로 진행하고 싶은 곳에서 continue만 입력하면 된다. while 문 안에서도 사용법이 동일하다. 다음은 for 문에서 continue 문을 사용한 예다.

코드 6-31 continue 문

for i in range(4):
    print('현재 반복 주기:', i)
    continue  # 현재 반복 주기를 중지하고 다음 주기로 넘어감
    print('다음 반복 주기:', i + 1)

실행 결과:

현재 반복 주기: 0
현재 반복 주기: 1
현재 반복 주기: 2
현재 반복 주기: 3

실행 결과를 보면 for 문의 본문 중 continue 명령 아래의 행들은 실행되지 않음을 확인할 수 있다.

break 문: 반복 전체를 중지

break 문은 반복의 한 주기만이 아니라 반복 전체를 중지시킨다. 사용법은 continue 문과 동일하고 for 문의 본문과 while 문의 본문 둘 모두에서 사용할 수 있다. 다음은 while 문에서 break 문을 사용한 예다.

코드 6-32 break 문

for i in range(4):
    print('현재 반복 주기:', i)
    break  # 반복 전체를 중지
    print('다음 반복 주기:', i + 1)

실행 결과:

현재 반복 주기: 0

실행 결과를 보면 break 명령이 실행되는 시점에서 반복 전체가 중지됨을 확인할 수 있다.

continue 문과 break 문의 활용

기본적인 반복 조건은 while 문과 for 문의 헤더에도 나타낼 수 있다. 하지만 반복 흐름을 본문 중간에 변경해야 할 때는 continue 문과 break 문이 필요하다. 다음 코드는 사용자로부터 입력받은 수를 계속 누적하여 합계를 출력하는 프로그램이다. 이 프로그램에서 break 문과 continue 문이 어떻게 사용되었는지 살펴보자.

코드 6-33 continue 문과 break 문의 활용

total = 0     # 사용자가 입력한 수의 합계

while True:   # 본문 코드를 무한 반복
    print('더할 수를 입력하세요(종료: \'그만\'): ', end='')
    user_input = input()      # 사용자 입력
    
    if user_input == '그만':  # '그만'이 입력된 경우,
        break                 # 반복을 종료
    
    if not user_input.isnumeric():  # 입력이 수가 아닌 경우,
        print('잘못된 입력입니다.')
        continue                    # 다음 주기로
    
    total += int(user_input)  # 사용자가 입력한 수를 더함
    print('합계:', total)     # 합계 출력

print('프로그램을 종료합니다.')

실행 결과:

더할 수를 입력하세요(종료하려면 '그만'): 500
합계: 500
더할 수를 입력하세요(종료하려면 '그만'): 2000
합계: 2500
더할 수를 입력하세요(종료하려면 '그만'): 안녕
잘못된 입력입니다.
더할 수를 입력하세요(종료하려면 '그만'): 10000
합계: 12500
더할 수를 입력하세요(종료하려면 '그만'): 그만
프로그램을 종료합니다.

이 프로그램의 while 문은 기본 반복 조건과 본문 중간에서 반복 흐름을 제어해야 하는 예외상황 두 가지를 처리하고 있다.

  • 기본 반복 조건: 사용자가 수를 몇 번이나 입력할지 모르므로, 반복 조건을 True로 하여 무한히 반복하도록 했다.
  • 예외상황 1: 사용자가 ‘그만’을 입력한 경우. break 문을 통해 반복을 중지한다. break 문 다음 행의 코드는 실행되지 않으며, 반복 자체도 종료된다. 무한 반복은 스스로를 멈추지 못하므로 break 문과 함께 사용되는 경우가 많다.
  • 예외상황 2: 사용자가 입력한 텍스트가 수가 아닌 경우. 이 경우에는 덧셈을 할 수 없으므로 수를 더하는 명령을 실행해서는 안 된다. 그래서 수를 더하는 명령을 수행하기 전에 ‘잘못된 입력입니다’를 출력한 후, continue 문으로 다음 주기로 넘어간다.

6.2.6 while 문과 for 문의 else 절

if 문에서 배운 else 절을 while 문과 for 문에서도 사용할 수 있다.

while 문에서의 else 절

while 문의 else 절은 if 문과 마찬가지로 조건이 거짓일 때 실행할 코드를 포함한다.

while(조건):
    본문 1 (반복 실행할 코드)
else:
    본문 2 (조건이 거짓일 때 실행할 코드)

다음은 while 문에 else 절을 포함한 예다.

코드 6-34 while … else … 문의 사용예

i = 0
while(i < 3):
    print(i, '번째 실행')
    i += 1
else:
    print('반복 완료')

실행 결과:

0 번째 실행
1 번째 실행
2 번째 실행
반복 완료

if 문에서는 조건이 참인지 거짓인지에 따라 else 절의 본문이 실행될 수도 그렇지 않을 수도 있다. 그런데 while 문은 조건이 거짓이 될 때 반복이 끝나므로, 반복이 끝날 때 항상 else 절의 본문이 실행될 것이라고 생각할 수 있다. 정상적으로 반복이 끝난 경우에는 그렇다. 하지만 break 문을 통해 반복이 종료된 경우에는 ‘조건이 거짓으로 평가’된 것이 아니므로 else 절의 본문이 실행되지 않는다.

코드 6-35 else 절이 실행되지 않는 경우

i = 0
while(i < 100):
    print(i, '번째 실행')
    i += 1
    if (i > 2):
        print('반복 중지')
        break
else:
    print('반복 완료')

실행 결과:

0 번째 실행
1 번째 실행
2 번째 실행
반복 중지

for 문에서의 else 절

for 문의 else 절은 전체 반복 과정이 정상적으로 종료된 직후에 본문의 코드를 실행한다. break 문으로 반복이 중지된 경우는 정상적인 종료가 아니므로 else 문의 본문이 실행되지 않는다. else 절을 포함한 양식은 다음과 같다.

for 변수 in 컬렉션:
    본문 1 (반복 실행할 코드)
else:
    본문 2 (반복이 정상 종료된 직후 실행할 코드)

다음은 for 문에 else 절을 포함한 예다.

코드 6-36 for … else … 문의 사용예

for i in range(3):
    print(i, '번째 실행')
else:
    print('반복 완료')

실행 결과:

0 번째 실행
1 번째 실행
2 번째 실행
반복 완료

반복에서 else 절의 활용

단순히 반복이 끝난 후에 코드를 실행해야 한다면 else 절을 사용할 필요 없이 그저 while 문과 for 문의 다음 행에 그 코드를 입력하면 된다. else 절의 가치는 반복이 정상적으로 종료되었는지 아니면 임의로 중지되었는지를 구분하는 데 있다. 다음은 이를 활용해 수 리스트에서 첫 번째 짝수를 찾아내는 프로그램이다.

코드 6-37 첫 짝수 찾기

def 첫_짝수_찾기(numbers):
    """numbers에서 첫 번째 짝수를 찾아 화면에 출력한다."""
    for n in numbers:
        if n % 2 == 0:
            print(n, '이 첫 짝수입니다.')
            break;
    else:
        print('짝수가 없습니다.')

첫_짝수_찾기([1, 3, 5, 33, 47, 55])
첫_짝수_찾기([7, 5, 6, 72, 19, 81])

실행 결과:

짝수가 없습니다.
6 이 첫 짝수입니다.

위 코드의 첫_짝수_찾기() 함수는 짝수가 중간에 발견되었을 때는 반복을 중지하며 발견된 짝수를 알려주고, 반복이 다 끝날 때까지(정상 종료시까지) 짝수를 찾지 못했을 때는 짝수가 없다고 알려준다. else 절을 활용하지 않았다면 별도의 변수를 사용해 짝수를 발견했는지 아닌지 기억해야 했을 것이다.

6.2.7 반복 중첩하기

코드 블록은 또다른 코드 블록을 포함할 수 있다. 그동안 def 문 안에서 if 문과 for 문을 사용해 보기도 했고, if 문 안에서 if 문을 정의한 표현도 보았다. 코드 블록의 중첩은 필요한만큼 여러 번 할 수 있다. 한 단계마다 들여쓰기를 네 번씩 해야 한다는 점, 같은 단계의 코드 블록은 같은 수준만큼 들여써야 한다는 점만 주의하면 된다. for 문과 while 문의 본문도 코드 블록이므로 반복을 서로 중첩하는 것도 가능하다. 반복을 중첩하면 표를 그리거나 2차원, 3차원, … n차원의 컬렉션을 순회하는 등의 작업을 나타낼 수 있다.

곱셈표 출력하기

다음은 for 문 두 개를 중첩하여 곰셈표를 출력하는 프로그램이다. 파이참에서 for_in_for1.py 파일을 만들고 다음 코드를 입력해 실행해 보자.

코드 6-38 for 문 중첩하기 (for_in_for1.py)

for i in range(2, 10):          # 바깥쪽 for 문: 2에서 9까지 순회
    for j in range(1, 10):      # 안쪽 for 문:   1에서 9까지 순회
        print(i * j, end='  ')  # i * j 출력
    print()                     # 줄바꿈

실행 결과:

2  4  6  8  10  12  14  16  18  
3  6  9  12  15  18  21  24  27  
4  8  12  16  20  24  28  32  36  
5  10  15  20  25  30  35  40  45  
6  12  18  24  30  36  42  48  54  
7  14  21  28  35  42  49  56  63  
8  16  24  32  40  48  56  64  72  
9  18  27  36  45  54  63  72  81  

코드 6-38이 실행되는 과정을 한 번 생각해보자.

  1. 바깥쪽의 for 문은 range(2, 10)을 순회하므로 본문의 코드는 8회 실행된다.
  2. 안쪽의 for 문은 바깥 쪽의 본문이 한 번 실행될 때마다 range(1, 10)을 순회한다. 바깥에서 8번, 안에서 9번 반복되므로 print(i * j, end=' ')은 총 8 * 9 = 72회 실행된다.
  3. 화면에 출력할 수는 i * j로 계산했는데, 이는 바깥쪽 for 문의 변수 i와 안쪽 for 문의 변수 j를 함께 사용한 것이다. i에 2가 대입된 동안, j에는 1, 2, 3, 4, …가 차례로 대입되므로, 화면에는 2, 4, 6, 8, … 이 차례대로 출력된다.
  4. 안쪽 for 문을 실행한 후에는 print() 명령으로 줄바꿈을 하도록 했다. 이 명령도 바깥쪽 for 문의 일부분이므로(들여쓰기로 구별할 수 있다) 총 8회 실행된다.

중첩 반복 표현은 중첩된 컬렉션을 순회할 때도 많이 사용된다. 복잡한 컬렉션을 순회하는 방법은 7장에서 좀 더 알아보기로 하자.

연습문제

연습문제 6-6 5의 배수의 합계 (while 문을 사용하여)

1백 이상, 1만 미만인 자연수 가운데 5의 배수를 모두 합하면 얼마인지 while 문을 사용해 계산해 보아라.

  • 힌트: 초기값이 1백이고 각 반복 주기마다 5씩 증가하는 변수를 사용해 보자.

연습문제 6-7 5의 배수의 합계 (for 문을 사용하여)

연습문제 6-6의 문제를 for 문과 range() 함수를 사용해 계산해 보아라.

연습문제 6-8 가장 큰 수 계산

리스트 하나를 매개변수로 전달받아, 리스트에서 가장 큰 원소를 반환하는 함수를 정의해 보아라. 이 함수는 max() 함수를 흉내내는 것이므로 max() 함수를 사용해서는 안 된다.

힌트: 가장 큰 수를 기억하기 위해 변수를 사용하자.

연습문제 6-9 소수 검사

약수가 1과 자기 자신 뿐인 자연수를 소수(prime number)라고 한다. 어떤 수를 입력받아 그 수가 소수인지 검사하여 True 또는 False를 반환하는 함수 is_prime()을 정의하라. 그 후, 정의한 함수를 이용해 1 이상 100 미만의 모든 소수의 합을 계산하라.

힌트: A를 B로 나눈 나머지가 존재한다면 B는 A의 약수가 아니다.