파이썬 강좌 – 반복문 ~ 비슷한 작업을 반복하기

  1. 프롤로그
  2. 개발 첫걸음
    1. 컴퓨터 구성요소 - 컴퓨터는 어떤 걸 할 수 있나?
    2. 개발과 관련된 용어
    3. 파이썬의 선택 - 왜 파이썬인가?
    4. 파이썬 설치 - Hello World 출력하기
    5. Visual Studio Code 의 편리한 기능
    6. REPL과 콘솔 창 - 파이썬 동작시키기
  3. 파이썬 기초
    1. 기초 입출력 - 소통하기
    2. 변수와 대입 - 기억하기
    3. 연산자 - 계산하기
    4. 조건문 - 분기를 만들기
    5. 반복문 - 비슷한 작업을 반복하기
    6. 변수와 리스트 - 비슷한 변수들을 묶기
    7. for, range - 리스트의 항목을 다루기
    8. 파이선 기초 꿀팁
      1. 함수와 메소드의 호출 - 편리한 기능 이용하기
      2. 모듈 설치와 사용 - 유용한 기능 끌어다 쓰기
      3. 문자열 - 텍스트 다루기
  4. 파이썬 중급
    1. 정규표현식 - 문자열을 검색하고 치환하기(작성중)
    2. 함수를 직접 만들기 - 자주 쓰는 기능을 묶기
    3. 딕셔너리, 튜플, 세트 - 변수를 다양한 방법으로 묶기
    4. 클래스와 객체 - 변수를 사람으로 진화시키기
    5. 상속 - 클래스를 확장하기
    6. 파이썬 중급 꿀팁
      1. 코드를 작성하는 사람의 의도(작성중)
      2. 정체성과 동질성 - 객체의 성질(작성중)
      3. 명령문, 표현식 - 문법을 이루는 것들 (작성중)
      4. 슬라이싱 - 리스트를 갖고 놀기
  5. 파이썬 고급
    1. 예외와 에러 - 예상치 못한 상황에 대응하기
    2. 특별 메소드와 연산자 - 파이썬의 내부 작동방식 이해하기
    3. 다양한 함수 인수 - 유연한 함수 만들기
    4. 시퀀스와 반복자 - 반복과 순회를 자유자재로 다루기
    5. 변수의 범위 - 이름 검색의 범위
  6. 파이썬 심화
    1. 제너레이터와 코루틴 -
    2. async와 await
    3. 데코레이터 - 함수의 기능을 강화하기
    4. 객체로서의 클래스 - 클래스를 동적으로 정의하기
  7. 실전 (파이썬 외적인 것들)
    1. 유니코드 - 컴퓨터에서 문자를 표기하는 방법
    2. html, css, 인터넷 - 자동화 첫 걸음 내딛기
    3. 네트워크 - 인터넷으로 통신하는 방법
    4. 문서 - 문맥을 읽어보기

때릴 수록 강력해지는 공격력

계속해서 새로운 개념을 배워나가고 있습니다. 이번 시간에는 반복문을 알아볼 겁니다. 역시나 예제로 시작합시다. 반복문의 필요성을 깨우쳐야 배울 힘이 나겠지요.

  • 슈퍼파워 게임회사는 플레이어가 훈련할 수 있는 샌드백을 만들고 있습니다.
  • 플레이어의 처음 공격력은 1밖에 되지 않지만 샌드백을 한 번 때릴 때마다 공격력이 1씩 증가합니다.
  • 샌드백은 공격받을 때마다 때린 사람의 공격력만큼 내구도가 낮아집니다.
  • 샌드백이 내구도가 0이 되면 샌드백은 부서집니다. (부서질 때에도 공격력은 성장합니다)
  • 샌드백의 내구도를 임의로 설정해보고 나서, 플레이어가 최종적으로 공격력이 몇인지 궁금합니다.

좋습니다. 샌드백의 내구도를 durability, 플레이어의 공격력을 attack이라 명명하고 코드를 짜 봅시다.

durability = int(input("샌드백의 내구도를 입력하세요 >> "))
attack = 1
durability -= attack
attack += 1
if durability <= 0:
    print("샌드백이 부서졌습니다! 최종 공격력은 " + str(attack) + "입니다.")
else:
    durability -= attack
    attack += 1
    if durability <= 0:
        print("샌드백이 부서졌습니다! 최종 공격력은 " + str(attack) + "입니다.")
    else:
        durability -= attack
        attack += 1
        if durability <= 0:
            print("샌드백이 부서졌습니다! 최종 공격력은 " + str(attack) + "입니다.")
        else:
            durability -= attack
            attack += 1
            if durability <= 0:
                print("샌드백이 부서졌습니다! 최종 공격력은 " + str(attack) + "입니다.")
            else:
                durability -= attack
                attack += 1
                if durability <= 0:
                    print("샌드백이 부서졌습니다! 최종 공격력은 " + str(attack) + "입니다.")

실행 결과는 다음과 같습니다.

----- 1번째 실행 -----
샌드백의 내구도를 입력하세요 >> 13
샌드백이 부서졌습니다! 최종 힘은 6입니다.
----- 2번째 실행 -----
샌드백의 내구도를 입력하세요 >> 17

위 코드는 조건문을 이용하여 계속해서 durabilityattack 만큼 차감시키고 attack1씩 증가시키고 있습니다. durability은 최대 5번 차감될 수 있습니다. 왜냐하면 durability -= attack 부분이 5번 밖에 등장하지 않으니까요. 그래서 플레이어 또한 5번 밖에 성장하지 못합니다. 내구도가 13이라면 5회의 성장 안에 샌드백을 부술 수 있지만 내구도가 17이라면 성장 횟수가 부족하여 샌드백을 부수지 못합니다. (샌드백이 부숴졌다는 메세지가 뜨지 않습니다.)

아니, 선생님 멍청하십니까? durability의 피해 값은 딱봐도 등차수열의 합이라서 durability = attack * (attack + 1) / 2 의 식을 풀어, 이차방정식을 만들어 양의 해를 구하기만 하면 끝날 문제 아닌가요? 어… 네 그렇게 생각 할 수도 있겠지만 매번 공식을 이용해 문제를 해결한다면, 상황이 바뀌었을 때 해당 공식을 매번 수정해야 하는 불편함이 있습니다. 지금은 직접 코드로 시뮬레이션한다고 생각하시면 좋을 듯 합니다.

여하튼 계속해서 반복하기에는 무리가 있습니다. 복사 붙여넣기는 만능이 아니지요. 만약 10000과 같은, 아주 큰 내구도를 집어넣는다면 도대체 얼마나 많이 복붙해야 할지 가늠도 오지 않습니다.

이러한 상황속에서 반복문은 빛나는 동아줄이 되어 우리를 구원해줄 수 있습니다.

다음 코드를 봐주세요.

durability = int(input("샌드백의 내구도를 입력하세요 >> "))
attack = 1
while durability > 0: ##a_1##
    durability -= attack
    attack += 1
print("샌드백이 부서졌습니다! 최종 공격력은 " + str(attack) + "입니다.")
----- 1번째 실행 -----
샌드백의 내구도를 입력하세요 >> 28
샌드백이 부서졌습니다! 최종 공격력은 8입니다.
----- 2번째 실행 -----
샌드백의 내구도를 입력하세요 >> 28371
샌드백이 부서졌습니다! 최종 공격력은 239입니다.

이게 무슨 일이죠? 코드의 양이 완전히 줄어들었습니다. 더군다나 28은 물론이고 28371 같은 아주 높은 숫자도 문제 없이 실행한다는 사실을 확인할 수 있습니다. 이만 팔천의 샌드백을 죽어라 때리고 때려서 공격력이 239까지 성장했다니! 그 사실을 바로 알 수 있다니. 정말 놀라울 따름입니다.

a_1(1.) 코드 설명

while durability > 0:

이 행이 이번 시간의 핵심입니다. while 이라는 새로운 키워드가 등장하였고, 그 바로 뒤에는 비교 연산자가 들어간 식이 들어갑니다. 행의 끝에 콜론(:)을 통해 새로운 블록을 시작한다고 말하는 것까지, 어찌 조건문과 굉장히 비슷하게 생겼습니다.

while의 동작 원리를 알려드리겠습니다. 뒤따르는 조건이 True 라면, 뒤따르는 블록을 실행합니다. 다시 돌아와 조건을 체크합니다. True라면 블록을 실행합니다. 다시 조건 체크 후 블록을 실행합니다. 계속, 무한 반복합니다. 조건이 False가 되어야 블록을 건너뛰고 다음으로 넘어갑니다.

while의 조건은 durability > 0 입니다. durability 변수이고 계속 우리가 차감시켜주고 있기 때문에 값이 계속해서 변합니다. 다음 코드는 바로 위 코드와 같지만, while 내부에 print 함수 하나를 추가하여 루프가 돌고 있는 시점에서 값이 어떻게 변화하는지 실시간으로 확인할 수 있도록 했습니다. 코드를 실행하여 결과를 확인해보세요.

durability = int(input("샌드백의 내구도를 입력하세요 >> "))
attack = 1
while durability > 0: 
    print("현재 공격력은 " + str(attack) + " 이며, 샌드백의 내구도는 " + str(durability) + " 입니다." )
    durability -= attack
    attack += 1
print("샌드백이 부서졌습니다! 최종 공격력은 " + str(attack) + "입니다.")

입력을 28371로 해보겠습니다.

(중략)
현재 공격력은 226 이며, 샌드백의 내구도는 2946 입니다.
현재 공격력은 227 이며, 샌드백의 내구도는 2720 입니다.
현재 공격력은 228 이며, 샌드백의 내구도는 2493 입니다.
현재 공격력은 229 이며, 샌드백의 내구도는 2265 입니다.
현재 공격력은 230 이며, 샌드백의 내구도는 2036 입니다.
현재 공격력은 231 이며, 샌드백의 내구도는 1806 입니다.
현재 공격력은 232 이며, 샌드백의 내구도는 1575 입니다.
현재 공격력은 233 이며, 샌드백의 내구도는 1343 입니다.
현재 공격력은 234 이며, 샌드백의 내구도는 1110 입니다.
현재 공격력은 235 이며, 샌드백의 내구도는 876 입니다.
현재 공격력은 236 이며, 샌드백의 내구도는 641 입니다.
현재 공격력은 237 이며, 샌드백의 내구도는 405 입니다.
현재 공격력은 238 이며, 샌드백의 내구도는 168 입니다.
샌드백이 부서졌습니다! 최종 공격력은 239입니다.

컴퓨터가 공식을 이용하거나 편법을 쓰지 않았다는 사실을 우리는 알 수 있습니다. 정말 순수하게 공격력을 1씩 올려가며 내구도를 닳게 하고 샌드백을 부서뜨립니다.


break

while은 조건을 검사해봤을 때 False가 나오는 순간 바로 반복문을 빠져나옵니다. 그렇다면 반복문을 빠져나올 방법은 조건에서 False가 나오도록 유도하는 방법 뿐일까요? 다행스럽게도 반복문을 빠져나올 수 있는 방법은 한 가지 더 있습니다. 바로 break를 이용하는 것입니다. 다음 예문을 참조해주세요.

durability = int(input("샌드백의 내구도를 입력하세요 >> "))
attack = 1
while True: 
    print("현재 공격력은 " + str(attack) + " 이며, 샌드백의 내구도는 " + str(durability) + " 입니다." )
    durability -= attack
    attack += 1
    if durability <= 0:
        break

print("샌드백이 부서졌습니다! 최종 공격력은 " + str(attack) + "입니다.")

break는 그냥 단독으로 break라고 씁니다. 파이썬 인터프리터가 break를 맞닥뜨리게 된다면, 그 즉시 while 블록 바깥으로 빠져나옵니다. while 조건과 상관없이, while문의 한복판에 있더라도 예외없이 동작합니다.

while True: 에서는, 이 while 블록은 무한 루프를 할 것이라고 명시하고 있습니다. True 값은 영원히 True 입니다. 하지만 while 블록 내부에서 break를 사용한다면 이 루프문을 빠져나갈 수 있는 열쇠가 되어 프로그램의 흐름을 제어할 수 있게 됩니다.


개념 정리

개념 정리의 시간입니다. 정리할 개념은 생각보다 많이 없습니다.

  • while 은 조건이 True 이면 while 블록을 실행한다. 실행을 끝마치고 조건 검사부터 무한 반복한다.
  • while문의 조건이 한번이라도 False 가 되면 반복을 종료한다.
  • break를 통해 while 문 중간에 즉시 중단할 수 있다.
  • while 내부에 print 함수를 추가하여 반복이 진행되고 있는 시점에서 값이 어떻게 변화하는지 실시간으로 확인할 수 있다.

반복문의 설계

반복문은 아주 강력한 도구입니다. 한두 줄의 코드를 써도 그것이 몇 백번, 몇 천번 반복하게 만들 수 있으니까요. 같은 코드를 몇 천줄 쓴다고 상상해보세요. 복사 붙여넣기 하는데만 한 세월일 것입니다.

하지만 반복문을 처음 맞닥뜨렸을 때 반복문을 어떻게 활용해야 할지 막막합니다. 언제 어떻게 써야 할지 감이 잘 오지가 않죠. 왜냐하면 반복문은 실제로 코드가 어떻게 실행되는지 상상하기가 어렵기 때문입니다. if문 같은 경우에는 코드가 실행되거나, 실행되지 않거나 입니다. 눈으로 따라가며 코드의 흐름을 어느정도 파악할 수 있습니다. 반복문은 한 번 반복할지, 세 번 반복할지, 혹은 100번 반복할지 모르는 법입니다.

반복문을 설계할 때 주로 생각해야 할 점은 다음과 같습니다.

  • 반복문에 들어가기 전에, 준비 작업은 어떻게 할까?
  • 조건을 어떻게 작성할까?
  • 반복문 내부에서 조건 변수를 어떻게 조정할까?
  • 반복문을 빠져나왔다는 건 무엇을 의미할까? 반복문 이후에는 어떻게 해야 하나?

반복문에 들어가기 전에 준비 작업은 어떻게 할까?

다음 코드는 위 예제에서 attack = 1while 블록 내부로 옮긴 코드입니다. 실행해볼까요? 아마도 우리의 의도대로 움직이지 않을겁니다.

durability = int(input("샌드백의 내구도를 입력하세요 >> "))
while durability > 0: 
    attack = 1
    print("공격력: " + str(attack) + ", 내구도: " + str(durability))
    durability -= attack
    attack += 1
print("샌드백이 부서졌습니다! 최종 공격력은 " + str(attack) + "입니다.")
샌드백의 내구도를 입력하세요 >> 10
공격력: 1, 내구도: 10
공격력: 1, 내구도: 9
공격력: 1, 내구도: 8
공격력: 1, 내구도: 7
공격력: 1, 내구도: 6
공격력: 1, 내구도: 5
공격력: 1, 내구도: 4
공격력: 1, 내구도: 3
공격력: 1, 내구도: 2
공격력: 1, 내구도: 1
샌드백이 부서졌습니다! 최종 공격력은 2입니다.

매 루프마다 attack += 1을 해주었음에도 불구하고 계속 현재 공격력이 1이라 출력하고 있습니다. 원인은 매 루프마다 실행되는 attack = 1 대입문입니다. 이전 루프에서 attack += 1 이 실행되어 attack2가 되었음에도 불구하고 다시 attack = 1로 대입해주고 있습니다. 결국 매 루프마다 공격력은 변하지 않은 채 내구도는 1씩 닳게 되겠죠.

attack에 대한 초기화는 while 내부에 존재하면 안 됩니다. 반복문 이전에 설정해야 할 초깃값이 되어야 논리가 맞아 떨어집니다.


조건을 어떻게 작성할까?

while에서 조건은 True일 경우에만 계속해서 동작합니다. 한 번이라도 False가 되면 while 문을 빠져나오게 되며, 다시는 돌아갈 수 없게 됩니다. 가장 처음으로 while에 맞닥뜨렸을 때 False 라면, 한 번도 실행되지 않은 채 다음으로 넘어갑니다. 즉, 반복문이 반복될 동안은 조건이 True로 유지되었다가, 종료하고 싶을 때 조건이 False로 바뀌기만 하면 된다는 것입니다.

반면 break는 어느 정도 반대의 논리를 따릅니다. break는 대개 if문 내부에 쓰이는 식으로 함께 쓰이는데, 이 때 if 조건은 False여야 내부의 break를 건들지 않아 종료되지 않고 계속해서 실행될 수 있으며, 한 번이라도 True가 된다면 break가 실행되는 결과를 낳아서 반복문이 종료됩니다.

while에서의 조건이나, ifbreak 에서의 조건이나 마찬가지로, 대개 조건에서는 변수를 활용해야 합니다. 왜냐하면 바뀌지 않는 조건은 결과 또한 True 또는 False로 고정되기 때문에, 노트북의 배터리가 방전될 때까지 무한히 반복하는 프로그램이 되거나 단 한번도 실행될 수 없는 무용지물이 되겠지요. 예를 들어, 다음 while은 쓸모가 없습니다.

# 무한히 반복합니다.
while 3 < 8:
    코드

# 절대 실행될 수 없습니다.
while 3 > 8:
    코드

# 영원히 종료되지 않습니다.
while True:
    코드
    if 3 > 8:
        break

# 마찬가지로 단 한번만 실행됩니다.
while True:
    코드
    if 3 < 8:
        break

# a < b 조건이 충족한다 하더라도 단 한번만 실행됩니다.
while a < b:
    코드
    break

조건에 어떤 변수를 넣느냐가 관건입니다.


반복문 내부에서 조건 변수를 어떻게 조정할까?

조건 변수를 조정한다는 게 어떤 의미일까요? 우리가 만든 while 문에서 조건은 durability > 0 입니다. 우리는 값을 직접 입력하여 durability의 초기값을 설정했습니다. 우리가 위에서 8000, 300이라고 입력한 것처럼요. 하지만 만약에 이 durability가 영원히 변하지 않는다면 어떻게 될까요? 지금은 while 문 내부에서 durability -= attack으로 지속적인 변화를 주고 있습니다만, 이 명령이 없다면 어떻게 될까요? 계속해서 while 문의 조건은 8000 > 0으로 고정되어 버릴테고, 우리의 루프는 영영 끝나지 않게 되어 버릴 겁니다.

조건을 아무리 잘 설정했다 하더라도, 실제 조건에 관여하는 변수들을 지속적으로 관리하고 조정해나가지 않는다면 조건은 무용지물입니다. 그러므로 변수들을 어떻게 변화시켜나갈 것인가 또한 중요한 화두입니다.

훌륭한 방법이 있습니다. 앞서 print("공격력: " + str(attack) + ", 내구도: " + str(durability)) 이 줄을 추가한 것처럼 변화하는 변수를 실시간으로 확인할 수 있도록 변수의 내용을 매 루프마다 출력시키는 것입니다. print 함수는 아무리 많이 써도 닳지 않습니다. 어떤 식으로 변수가 변화하는지 파악하기에 가장 좋은 방법입니다.


반복문을 빠져나왔다는 건 무엇을 의미할까? 반복문 이후에는 어떻게 해야 하나?

마지막 예제에서 우리는 두 가지 시나리오대로 루프문을 빠져나왔습니다. 첫째는 while문의 조건에 의하여 durability0보다 크지 않다면 빠져나오는 경우이고, 둘째는 attack100 이상이라면 break에 의하여 즉시 빠져나오는 경우입니다. 앞서 말했듯이 breakelse 블록을 실행시키지 않고 완전히 빠져나옵니다.

위 예제는 마지막으로 할 일을 특별히 구상하지 않았기 때문에 반복문 이후에 특별히 할 일은 없습니다. 그냥 최종 공격력을 print하고 끝을 냈습니다.

반복문은 대개 엄청 많이 일을 하기 때문에 반복문을 거쳐 온 결과가 기대했던 것과는 상당히 다를 수도 있습니다. 반복문의 역할을 명백하게 만들고 반복문을 빠져나왔다는 것이 어떤 의미를 가져야 하는지 상기하면 좀 더 쉽게 반복문을 만들 수 있을 것입니다.


실전

처음에는 감이 오지 않습니다. 다음 문제를 풀어보도록 합시다.

수를 입력받고 이 수의 모든 약수를 출력하는 프로그램을 작성하여라.

처음에는 이것을 반복문을 써야 하는지 아닌지도 감이 오시지 않을 겁니다.


컴퓨터가 이해할 수 있는 식을 생각하기

우리는 약수가 무엇인지는 대충 압니다. ab로 나눴을 때 딱 나누어 떨어진다면 ba의 약수입니다. 하지만 나누어 떨어진다는 것은 무엇일까요? 우리는 저 문장으로도 뜻이 바로 통해버리기에 쉽게 생각하지만 컴퓨터는 컴퓨터의 언어로 생각하기 때문에 딱 나누어 떨어진다라는 말을 이해할 수 없습니다. 그렇기에 컴퓨터가 이해할 수 있는 말로 바꾸어줘야 합니다.

우리는 처음에 입력을 받는다고 했습니다. 그러므로 a는 이미 정해진 상황입니다. b는 임의로 우리가 4라고 정해봅시다. 그렇다면 딱 나누어 떨어진다라는 것을 어떻게 코드로 적어줄 수 있을까요? 다음과 같이 하면 될 것 같습니다.

a = int(input())
b = 4 # 임시로 적었습니다.
if a % b == 0:
    print(str(b) + '는 ' + str(a) + '의 약수입니다!')
else:
    print(str(b) + '는 ' + str(a) + '의 약수가 아닙니다!')
12
4는 12의 약수입니다!

이 단계에서의 핵심은 a % b == 0 이라고 적은 것입니다.


상황을 좁혀 가정하기

계속해서 상상력을 발휘해봅시다. 412의 약수임은 당연합니다. 우리가 될만한 수인 4를 적었기 때문이지요. 그렇다면 컴퓨터 입장에서 생각해봅시다. 컴퓨터는 124로 나누어 나머지를 구해보기 전까지는 4가 약수인지 아닌지 판단할 수 없습니다. 3이나 2도 마찬가지겠지요.

우리가 될만한 수를 후보군 이라고 이야기합시다. 1, 2, 3, 4, 5 등은 12에서 나눴을 때 약수가 될 가능성이 있는 수라고 우리가 정의내릴 수 있습니다. 반면 0, -10, 10012보다 한참 크거나 1보다 작다는 이유로 우리가 후보군에서 제외할 수 있습니다. 10012의 약수가 될 수 있을까에 대한 물음은 처음부터 성립하지 못합니다. 이런 범위에 있는 수들은 죽어도 약수가 될 수 없는 수들이기 때문입니다.

우리는 후보군을 아주 쉽게 생각합니다. 하지만 컴퓨터는 그렇게 하지 못합니다. 0이든 -10, 100이든 12의 약수가 될 수도 있으니 검사를 시도해보겠다고 발버둥칠 가능성이 높습니다.

후보군도 우리가 정해주어야 합니다. 상황을 만들어줘야 한다는 것이죠. 그러니까 그러한 그러므로 약수가 될만한 수들을, a % b == 0에서 b에 집어넣어 주어야 할 수들의 범위를 생각해보는 것이지요. 지금 상황에서는 1 이상 a 이하인 수라고 이야기할 수 있겠습니다.

이 단계에서의 핵심은 1 이상 a 이하의 수가 a의 약수가 될 가능성이 있다 고 이야기할 수 있다는 것입니다.


하드코딩하여 변하는 부분과 변하지 않는 부분 찾기

이제서야 비로소 코딩을 할 수 있겠습니다. 컴퓨터에게 일을 시킵시다. 하드코딩이란 무작정 복사 붙여넣기 등의 고전적인 방법을 이용하여 하드하게 코딩한다는 뜻입니다.우선 a의 입력을 마찬가지로 12라고 가정하도록 하겠습니다.

a = int(input())
b = 1
if a % b == 0:
    print(str(b) + '는 ' + str(a) + '의 약수입니다!')
b = 2
if a % b == 0:
    print(str(b) + '는 ' + str(a) + '의 약수입니다!')
b = 3
if a % b == 0:
    print(str(b) + '는 ' + str(a) + '의 약수입니다!')
b = 4
if a % b == 0:
    print(str(b) + '는 ' + str(a) + '의 약수입니다!')
b = 5
if a % b == 0:
    print(str(b) + '는 ' + str(a) + '의 약수입니다!')
b = 6
if a % b == 0:
    print(str(b) + '는 ' + str(a) + '의 약수입니다!')
b = 7
if a % b == 0:
    print(str(b) + '는 ' + str(a) + '의 약수입니다!')
b = 8
if a % b == 0:
    print(str(b) + '는 ' + str(a) + '의 약수입니다!')
b = 9
if a % b == 0:
    print(str(b) + '는 ' + str(a) + '의 약수입니다!')
b = 10
if a % b == 0:
    print(str(b) + '는 ' + str(a) + '의 약수입니다!')
b = 11
if a % b == 0:
    print(str(b) + '는 ' + str(a) + '의 약수입니다!')
b = 12
if a % b == 0:
    print(str(b) + '는 ' + str(a) + '의 약수입니다!')
12
1는 12의 약수입니다!
2는 12의 약수입니다!
3는 12의 약수입니다!
4는 12의 약수입니다!
6는 12의 약수입니다!
12는 12의 약수입니다!

엄청난 복사 붙여넣기의 향연입니다. 일단 우리의 의도대로 프로그램은 작동합니다. 이 프로그램은 12 이하의 수에서는 잘 작동합니다. 여기서 우리가 변하는 부분을 체크해봅시다.

b의 값이 게속 변합니다. 각 단계별로 1만큼 더해집니다. 우리가 이전에 b 후보군의 범위를 1 이상 a 이하라고 했었지요. 그 안에서 우리가 원하는 만큼 잘 움직입니다.

이 단계에서의 핵심은 b가 매 단계마다 1씩 더해진다 입니다. 이제 그 단계를 반복문으로 옮겨볼 차례입니다.


반복문의 설계 떠올리기

위에서 생각한 것 토대로 더 머리를 굴려봅시다. b1부터 a까지 1씩 더해집니다. 이를 아래에 대응시켜봅시다.

  • 반복문에 들어가기 전에, 준비 작업은 어떻게 할까?
    • b = 1
  • 조건을 어떻게 작성할까?
    • b <= a
  • 반복문 내부에서 조건 변수를 어떻게 조정할까?
    • b += 1
  • 반복문을 빠져나왔다는 건 무엇을 의미할까? 반복문 이후에는 어떻게 해야 하나?
    • 생각할 필요 없다. 매 단계마다 print를 할 것이기 때문이다.

반복문 작성하기

좋습니다. 이제 그대로 작성해봅시다.

a = int(input())
b = 1
while b <= a:
    if a % b == 0:
        print(str(b) + '는 ' + str(a) + '의 약수입니다!')
    b += 1
12
1는 12의 약수입니다!
2는 12의 약수입니다!
3는 12의 약수입니다!
4는 12의 약수입니다!
6는 12의 약수입니다!
12는 12의 약수입니다!

성공했습니다! 훨씬 큰 숫자를 써도 잘 작동하는 것을 확인하실 수 있을 겁니다.

코드 전체에서 조건식은 2개가 등장했습니다. 하나는 while문에 들어간 b <= a이고 하나는 if문에 들어간 a % b == 0입니다. 이 두 개의 구분은 유념해주시기 바랍니다. 후보군을 순회하는 것과 실제 조건이 들어맞는지 아닌지 체크하는 것은 별도의 작업이기 때문입니다.


else

이제껏 잘 써왔던 예문을 살짝 수정하고자 합니다. 다음 예시는 공격력이 100까지 되었다면 단련이 충분하다 판단하고, 그만두면서 남은 내구도를 출력합니다. 그리고 100이 되기 전에 샌드백이 부셔졌다면 샌드백이 부서졌다고 출력합니다. 또한 둘 중 어느 경우라도 최종 공격력을 출력합니다.

durability = int(input("샌드백의 내구도를 입력하세요 >> "))
attack = 1
while durability > 0: 
    print("공격력: " + str(attack) + ", 내구도: " + str(durability))
    durability -= attack
    attack += 1
    if attack >= 100:
        print("충분히 단련했습니다. 남은 내구도는 " + str(durability) + "입니다.")
        break
else: ##b_1##
    print("샌드백이 부서졌습니다!")
print("최종 공격력은 " + str(attack) + "입니다.")

8000을 입력했습니다.

(중략)
공격력: 97, 내구도: 3344
공격력: 98, 내구도: 3247
공격력: 99, 내구도: 3149
충분히 단련했습니다. 남은 내구도는 3050입니다.
최종 공격력은 100입니다.

300을 입력했습니다.

(중략)
공격력: 22, 내구도: 69
공격력: 23, 내구도: 47
공격력: 24, 내구도: 24
샌드백이 부서졌습니다!
최종 공격력은 25입니다.

b_1(1.) 코드 설명

else가 등장했습니다. else는 앞서 조건문에서 등장한 바 있습니다. 하지만 이 elseif와 짝지어지지 않고 while과 짝지어졌습니다. 이게 뜻하는 바는 무엇일까요?

while과 짝지어진 elseif와 비슷하게 작동되는데요, while에 붙어 있는 조건식을 검사할 때, 이 값이 False가 나온다면 여기 else 블록을 실행하도록 합니다. 즉 조건에 의해 루프문을 빠져나왔을 때 else 블록이 실행되는 것이죠.

여기서 유의해야 할 점은 break를 통해 루프문을 빠져나왔을 때에는 이 else 블록이 실행되지 않는다는 점 입니다. elsewhile에 붙어 있는 조건이 거짓일 때에만 실행되지, break와는 전혀 인연이 없다는 점을 꼭 명심해주세요.

보통의 다른 프로그래밍 언어에서는 elseif와 같은 조건문에나 있지 while문 같은 반복문에는 잘 없습니다. 반복문에서 else를 사용할 수 있다는 점은 파이썬만의 특징이라고 볼 수 있습니다. 반복문에 붙어있는 else를 통해 break로 빠져나왔는지 그렇지 않은지 곧바로 체크할 수 있기 때문에 종종 쓰이곤 합니다.

위 프로그램에 대한 흐름도를 그리면 다음과 같습니다.

graph TD i1["durability = int(input('샌드백의 내구도를 입력하세요 >> '))<br>attack = 1"] i2["attack = 1"] i1-->i2-->c c{"durability > 0"} c-->|True|p p["print('공격력: ' + str(attack) + ', 내구도: ' + str(durability))<br> durability -= attack<br> attack += 1"] p-->c2 c2{"attack >= 100"} c2-->|True|br["print('충분히 단련했습니다. 남은 내구도는 ' + str(durability) + '입니다.')<br> break"]-->lp lp["print('최종 공격력은 ' + str(attack) + '입니다.')"] c2-->|False|c c-->|False|l["print('샌드백이 부서졌습니다!')"] l-->lp class i1,i2,c,p,br,c2,l,lp code

샌드백 최종 코드에 대한 순서도


여담

이러한 순서놀이를 왜 하는 건지 이해가 되지 않는 분들도 있으실 겁니다. 이후에는 사실 for 반복문이라는, 더 직관적이고 간편한 방법도 있지요. 하지만 동작 원리를 바탕으로 실제 동작 과정을 머릿속으로 상상하는 추론력을 기르셔야 합니다. 그 방법은 while 이 참 좋다고 생각합니다. 간단한 원리이지만 아주 깊은 생각을 해야 실제 동작을 상상해낼 수 있기 때문입니다. 앞으로는 더 편한 함수, 더 편한 모듈을 사용해나가며 한결 수월하게 문제를 해결해나갈 것입니다만, 이러한 추론력이 뒷받침 되어주어야, 가지고 있는 방법들을 비로소 효율적으로 활용할 수 있게 될 것입니다.


연습 문제

  • 반복문은 왜 사용하는가?
  • while의 조건이 무엇일때 while 블록이 실행되는가?
  • while 중간에서 즉시 루프를 마치려면 어떻게 해야 하는가?
  • 반복문이 진행중일 때 그 상황을 알고 싶다면 어떻게 해야 하는가?
  • 언제 break를 사용하는가?
  • 반복문을 설계할 때 생각해야 할 점 네 가지는 무엇인가?
  • 반복문을 실제로 작성하고자 할 때 다섯 가지 단계는 무엇인가?
  • while과 짝지어진 else는 무슨 역할인가?
  • while과 짝지어진 else는 언제 실행되지 않는가?

프로그래밍 문제

  1. 다음과 같이 출력하는 프로그램을 만들어라.


    하하
    하하하
    하하하하

    하하하하하하하하

  2. 작은 수 하나와 큰 수 하나를 입력받고, 그 사이에 있는 모든 짝수를 출력하는 프로그램을 만들어라.

  3. 5개의 양의 정수를 입력받고, 이들 중 가장 큰 수를 출력하여라.

  4. 학생 10명의 점수를 각각 입력받고, 그 합계를 출력하라.

  5. 수를 계속 입력받아라. 입력받은 수는 계속해서 곱한다. 그 값이 1000이 넘어간다면, 그 값을 출력하고 프로그램을 종료하라.

  6. 수를 계속해서 입력받는다. 5 이상의 수가 3번째로 입력되는 순간 입력을 멈추고, 지금까지 입력받았던 모든 수를 합하여 출력하라.

  7. 다음 수식을 계산하고 그 결과를 출력하는 프로그램을 작성하여라. S = 1 + 2 + 4 + 7 + 11 + 16 + 22 + ... + 191

  8. 먼 미래, 인류는 치명적 바이러스 때문에 멸종 위기에 처했다. 인구는 50,000명 밖에 남지 않았으며, 이마저도 매년 10%씩 감소하는 추세이다. 이윽고 20,000명 이하가 될 때까지 몇 년이 걸리는지 계산하여라.

  9. 피보나치 수열은 앞 두개의 숫자는 1이며 다음 숫자는 바로 이전 숫자 두 개의 합이다. (1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...) 사용자로부터 양수 N을 입력받고, 피보나치 수열의 N 번째 수를 출력하여라.


프로그래밍 문제 정답

  1. 코드입니다.

    msg = ''
    i = 0
    while i < 8:
        msg += '하'
        print(msg)
        i += 1
    
  2. 코드입니다.

    low = int(input('작은 수를 입력하세요 >> '))
    high = int(input('큰 수를 입력하세요 >> '))
    
    i = low
    while i <= high:
        if i % 2 == 0:
            print(i)
        i += 1
    
  3. 코드입니다.

    i = 0
    max_num = 0
    while i < 5:
        num = int(input("숫자를 입력하세요 >> "))
        if max_num < num:
            max_num = num
        i += 1
    print('최댓값은', max_num, '입니다')
    
  4. 코드입니다.

    result = 0
    i = 1
    while i <= 10:
        result += int(input(str(i) + '번째 학생의 점수를 입력하세요 >> '))
        i += 1
    print('합계는', result, '점 입니다.')
    
  5. 코드입니다.

    result = 1
    while True:
        result *= int(input())
        if result > 1000:
            print(result)
            break
    
  6. 코드입니다.

    count = 0
    result = 0
    while True:
        num = int(input())
        result += num
        if num >= 5:
            count += 1
        if count >= 3:
            break
    print(result)
    
  7. 코드입니다.

    num = 1
    ac = 1
    result = 0
    while num <= 191:
        result += num
        num += ac
        ac += 1
    print(result)
    
  8. 코드입니다.

    people = 50000
    year = 0
    while True:
        people *= 0.9
        year += 1
        if people <= 20000:
            break
    print(year, "년이 지났습니다.")
    
  9. 코드입니다.

    i = int(input("숫자를 입력하세요 >> "))
    if i == 1 or i == 2 :
        print(1)
    else:
        s1 = 1
        s2 = 1
        j = 3
        while j <= i:
            s3 = s1 + s2
            s1 = s2
            s2 = s3
            j += 1
        print(s3)
    

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다

Scroll to top