본문 바로가기
Python/파이썬 알고리즘

[파이썬 알고리즘 #5] 반복문

by 서원두 2022. 8. 10.

반복문에 대해서 알아보고자 한다.

 


반복문은 무엇인가?

말 그대로 반복을 하게 해 준다. 기계가 가장 잘하는 반복은 이 반복문에서 온다 봐도 무방하다.

브라이언 대니얼슨이 랜디 오턴에게 계속 러닝 니를 날리긴 힘들지만 컴퓨터라면 가능하다

간단하게 0부터 N까지 돌려주는 경우도 있고, list 안의 요소들을 불러올 수도 있다. 아니면 조건을 달아서 그 조건이 충족되지 않을 때까지 반복하는 경우도 있다.

앞의 경우가 for문, 뒤의 경우가 while문이다. 이 둘은 비슷하지만 의미의 차이가 약간 존재한다.

 

반복문 1 : for문

for문의 핵심은 모든 요소를 돌린다는 것에 있다.

예를 들어 아래의 경우를 살펴보자.

for i in range(5):
# for i in range(0, 5, 1):
    print(i)
    
# Result
0
1
2
3
4

 

이 코드는 for문의 아주 핵심적이며 기초적인 range문을 for문에 넣은 것이다. range문 안의 숫자만큼 돌지만, 알다시피 파이썬은 0부터 세기 때문에 0부터 4까지만 출력된다.

두 번째 줄에 있는 코드는 range문 안의 조건을 더 채워서 쓴 것인데, 첫 번째 인자는 시작하는 수, 두 번째는 끝나는 수, 세 번째는 스킵하는 수다. 즉, i를 0부터 1씩 증가시키면서 5가 되기 전까지 반복을 돌린다고 보면 된다. 만약 2 이상의 수만큼 뛰어넘으면서 반복을 돌려야 한다거나, 역으로 수를 낮추면서 돌려야 할 때에는 이 방법을 사용해야 한다.

for i in range(4, -1, -1):
	print(i)
    
# Result
4
3
2
1
0

 

이것이 for문의 사실상 끝이라고 봐도 무방하다. 진짜다!

 

반복문 2 : while문

반복문에 조건을 달아야 할 때가 존재한다. 예를 들어 특정 변수의 크기가 어느 정도 커졌을 때 같은 경우다. 이럴 때에는 while문을 사용한다.

a = 0
while a < 5:
    print(a)
    a += 1
    
# Result
0
1
2
3
4

 

while 뒤에는 조건문이 들어가며, 그 조건이 참일 동안에만 while문이 돌아간다.

이 때문에 매우 조심해야 하는 부분이 있는데, 바로 while을 잘 못 다루면 무한 루프에 빠지기 쉽다는 것이다.

방금 위의 코드에서도 a += 1 코드가 없었다면 이 프로그램은 평생 끝나지 않는다. 왜냐면 a가 0인데 아무런 변화가 없기에 a < 5를 계속 만족하기 때문이다. 그렇기 때문에 꼭 조건문과 조건문 안의 변수 처리를 잘해줘야 한다.

 

무한 루프

while문은 조건문이라는 특성상 무한 루프를 쉽게 만들 수 있다.

무한 루프는 말 그대로 무한히 반복되는 루프를 의미한다. 무한 루프가 프로그램을 잘못 만들면 생길 수 있는 것이기 때문에 경계해야 하는 것은 맞지만, 우리가 일부러 만들어야 하는 경우도 당연히 존재한다.

물론 우리는 이런 식으로의 루프를 만들지 않아야 한다

대신 이때에는 반드시 특정 입력이 들어오는 경우에 무한 루프를 빠져나오게 하는 식으로 제어를 해줘야 한다.

위의 문제를 예시로 들어보자. 0 0이 아닌 모든 입력에는 더하는 연산을 하고, 0 0이 입력으로 들어오는 순간 종료를 시킨다. 이는 for문으로 절대 불가능하다.

이럴 때 쓰는 것이 바로 while문을 이용한 무한 루프다.

while True:
    A, B = map(int, sys.stdin.readline().rstrip().split())
    if A == 0 and B == 0:
        break
    
    print(A+B)

 

while True: 의 뜻이 무한 루프다. 어떤 변수가 어쨌건 간에 신경 쓰지 않고 항상 참(True)이기 때문이다.

다만 무한 루프여도 프로그램이 종료가 되어야 하기에 문제에서 제시한 것이 0 0이 입력되었을 때 종료하라는 지문이다. 이를 무한 루프 안에 조건문을 하나 더 걸어서 그 경우에 break를 하면 된다.

 

break와 continue

반복문에서 조건문과 결합될 때 자주 쓰일 구문이 바로 break와 continue다. 단어의 뜻 그대로 반복문을 부순다(break)던지, 계속 이어주게 한다(continue)던지 해준다.

이를 조금 더 파헤쳐 보자.

if __name__ == '__main__':
    for i in range(3):
        for j in range(4):
            if j == 3:
                print(i, j)
                break

        print(i)

 

# Result
0 3
0
1 3
1
2 3
2

 

break의 주의할 점은 break를 감싸는 반복문만을 종료시켜준다! 만약 중첩되어 있다면, 다른 중첩 반복문은 종료되지 않는다. 이는 위의 예시를 통해 쉽게 확인할 수 있다.

만약 중첩 반복문조차 끝내야 한다면 아래의 방법을 고민해보자. 내가 PS를 할 때에는 아래의 두 방법을 사용한다.

# 1. bool 변수를 이용한 탈출
if __name__ == '__main__':
    is_break = False
    for i in range(3):
        for j in range(4):
            if j == 3:
                print(i, j)
                is_break = True
                break
        
        if is_break:
            break
        print(i)
        
        
# 2. 문제에서 원하는 값만 출력하고 프로그램을 강제 종료시키기
if __name__ == '__main__':
    for i in range(3):
        for j in range(4):
            if j == 3:
                print(i, j)
                exit()
                
        print(i)

 

1번의 경우엔 중첩이 적고 그다음 작업을 해야 하는 경우가 존재할 때 쓰는 방법이다.

2번의 경우엔 마지막에 돌리는 중첩문이고, 출력만 하고 더 이상 프로그램을 돌릴 필요가 없을 때 쓰는 방법이다. 이 때는 break를 하지 않고 프로그램을 강제로 끝내기 위해 exit문을 사용하였다.


다음으로 continue문에서 주의해야 할 사항을 알아보자.

if __name__ == '__main__':
    for i in range(3):
        for j in range(4):
            if j < 3:
                continue

            print(i, j)
            break

        print(i)
        
# Result
0 3
0
1 3
1
2 3
2

 

만약 for문 안에서 continue문을 만나면 해당 for문의 뒤에 있는 모든 코드는 실행되지 않고 다음 반복으로 넘어간다!

즉, countinue를 만난 이후의 for문 안의 모든 코드들은 무시된다는 뜻이다. continue문을 다룰 때에는 이 특성 때문에 매우 조심해야 한다.

 

for문과 while문의 차이

사실 이 둘의 차이는 없는 수준이다. 하지만 이 둘은 차이가 없는 것은 절대 아니다.

알다시피 for문은 iterable한 객체가 있어야 하지만, while문에서 더 간결하게 반복문의 조건을 걸고 다룰 수 있다. 즉, for문은 일종의 틀에 박혀서 반복문을 돌리며, while은 특정 조건일 때 자유롭게 반복문을 빠져나갈 수 있다.

또한 빅오 표기법에서는 약간의 차이를 보인다. $ k \leq N$인 경우, for문의 빅오는 $ O(N) $이고, while문의 빅오는 $ O(k) $다. 이 사소한 차이점을 이해한다면 for문과 while문을 더 쉽게 다룰 수 있으리라 생각한다.

이와 관련하여 좋은 문제를 추천한다.

 

심화 : iterable한 객체를 이용해 반복문을 더 알차게 쓰기

파이썬에서는 iterable한 객체(리스트 등)를 이용해서 더 쉽게 요소에 접근하거나 조건문을 사용할 수 있다.

for문에서 쓰는 트릭은 아래와 같다.

if __name__ == '__main__':
    a = list(range(3))
    for now_a in a:
        print(now_a)

 

# Result
0
1
2

 

for문에서는 이런 식으로 사용 가능하다. 리스트같이 iterable한 객체에서 포인터(a[0]과 같이)를 지정하면서 가져오는 것이 아닌 직접 요소를 뽑아내서 쓸 수 있다.

다음은 while문에서 사용 가능한 트릭이다.

if __name__ == '__main__':
    a = list(range(3))
    while a:
        print(a.pop())

 

# Result
2
1
0

 

일단 여기서 사용된 pop 메서드는 제일 마지막 인덱스의 요소(element)를 빼내는 역할을 해준다. 빼낸 이후에는 그 리스트 안에는 존재하지 않게 된다. 이는 나중에 스택(stack)이라는 자료구조를 배울 때 상세히 설명할 것이다.

위의 경우에는 a라는 리스트 안에 요소가 계속 존재하는 동안에 print문을 실행하는 코드다.

a 안에 요소가 계속 존재한다면 while문 안의 코드가 실행되고, 더 이상 요소가 존재하지 않는다면 while문의 조건문은 거짓이 되어 반복문이 끝나게 된다.


이 두 테크닉은 PS던 어디던 간에 매우 자주 사용되므로 꼭 알아두자.


 

마지막의 심화 테크닉은 꼭 알아두는 것이 매우 중요하다.

728x90

댓글