회돌이 - 또는 자신을 반복하는 기술!

우리는 무엇을 다룰 것인가?
반복적인 타이핑을 줄이기 위해 회돌이를 사용하는 법. 회돌이의 여러형태와 그것들을 언제 사용하는가.

지난 연습문제에서 우리는 곱셈표 12단의 일부를 출력해 보았다. 그러나 그것은 많은 타이핑이 필요하고 우리가 그것을 확장할 필요가 있다면, 대단히 시간이 많이 필요할 것이다. 다행스럽게도 더 좋은 방법이 있어서 프로그래밍 언어가 우리에게 제공하는 실제적인 파워를 바로 거기에서 보기 시작한다.

FOR 회돌이

우리가 하려고 하는 것은 프로그래밍 언어에게 반복을 하도록 시켜서, 반복할 때마다 값이 증가하는 변수를 대체하는 것이다. 파이썬에서 그것은 다음과 같은 형태가 된다:

>>>for i in range(1,13):
...    print "%d x 12 = %d" % (i, i*12)

주의 1: 우리는 13을 특정하기 위하여 range(1,13)를 필요로 한다. 왜냐하면 range()함수는 첫번째 숫자로부터 생성하기 시작하여 두 번째 값은 포함하지 않고, 두번째 숫자까지 생성한다. 이것은 처음에는 약간은 괴이하게 보이지만 거기에는 이유가 있으며 여러분은 그것에 익숙해져야만 한다.

주의 2: for 연산자는 파이썬에서 실제로는 foreach 연산자인데 그 연산자로 하위 연속코드를 한 집합의 각각의 구성원에다가 적용한다. 이 경우에 그 집합은 range()가 생성한 숫자들의 리스트이다. 여러분은 print range(1,13)를 파이썬 프롬프트에서 타이프해보고 무엇이 인쇄되는 가를 보면 그것을 확인할 수 있다.

주의 3: print라인은 위에 있는 for 라인보다 더 들여쓰기 되어있다. 그사실은 대단히 중요한 요점인데 왜냐하면 그것이 바로 파이썬이 print문이 반복될 부분이라는 것을 알수 있는 방법이기 때문이다. 여러분이 얼마만큼의 들여쓰기를 했느냐는 일관성을 유지하는 한 중요하지 않다.

그래서 어떻게 그 프로그램은 작동하는가? 그것을 죽 거닐어 보자.
먼저, 파이썬은 range함수를 사용하여 숫자의 리스트를 1 에서 12 까지 생성하였다.

다음으로 파이썬은 i 를 리스트에서 첫 번째 값과 같도록 만든다. 이 경우에는 1 이다. 그리고 나서 그것은 들여쓰기된 부분의 코드를 'the value i = 1' 를 사용하여 실행한다:

   print "%d x 12 = %d" % (1, 1*12)

파이썬은 그리고는 for 라인으로 다시 돌아가서 i를 리스트에서 다음 값으로 설정한다, 이번에는 2 이다. 그것은 또 다시 들여쓰기된 코드를 이번에는 i=2 를 가지고, 실행한다:

   print "%d x 12 = %d" % (2, 2*12)

이러한 과정을 반복하기를 i 의 값이 리스트에 있는 모든 값들로 설정될 때까지 계속한다. 바로 그 점에서 그것은 들여쓰기 되지 않은 다음의 명령어로 이동한다. - 이 경우에는 다른 명령어들이 없으므로 프로그램은 멈춘다.

여기에 똑 같은 회돌이를 베이직으로 구현한다:

FOR I = 1 to 12
    PRINT I, " x 12 = ", I*12
NEXT I

이것은 더욱 더 명료하고 쉽게 무슨일이 일어나는지 보여준다. 그렇지만 파이썬 버젼은 우리가 숫자들의 집합, 리스트의 항목들 또는 다른 어떤 집합들(예: 문자열)에 관하여서도 회돌이를 할 수가 있다는 점에서 더욱 유연하다.

그리고 Tcl 에서

Tcl 은 for 구조를 사용하는데 그것은 많은 프로그래밍 언어에서 일반적인 것으로 C 가 대표적이다. 그것은 다음과 같이 보인다:


for {set i 1} {$i <= 12} {incr i} {
    puts [format "%d x 12 = %d" $i [expr $i*12]]
    }

주의 : 이 구조는 3 개의 부분으로 나누어진다:

회돌이 몸체는 오직 점검부분이 참일 때만 실행될 것이다. 이러한 부분들 각각은 임의의 코드를 포함할 수 있지만 그러나 점검부분은 반드시 불리언 값으로 평가되야 한다.(그것을 Tcl에서는 0이든가 0이 아닌가로 나눈다.) 내가 들여쓰기된 회돌이 몸체를 보여 주었지만 이것은 순전히 이해를 돕기 위한 것임을 주의하라. Tcl은 블록을 들여쓰기 위해 '나'를 필요로 하지 않는다, 그 보다는 괄호가 시작과 마지막을 표시하기위해 사용된다.

Tcl은 또한 foreach구조를 가지고 있어서 리스트에 적용될 수 있다.

WHILE 회돌이

FOR 회돌이는 가능한 오직 유일한 회돌이 구조는 아니다. 그것은 단지 FOR 루프는 수행하기를 원하는 반복의 횟수를 알려 주도록 혹은 먼저 계산할수 있도록 우리에게 요구한다 무슨일인가 일어날 까지 우리가 특정한 작업을 계속하고자 할때 그러나 언제 그 일이 일어날지 알지 못한다면 그러면 무슨일이 일어나는가? 예를 들어, 우리는 파일로 부터 데이타를 읽고 처리하기를 원할 수도 있다, 그러나 우리는 먼저 얼마나 많은 데이타항목을 파일이 포함하고 있는지 알지 못한다. 우리는 파일의 끝에 도달할 때까지 단지 데이타를 처리하기를 원할 뿐이다. 그것은 가능하기는 하지만 FOR 회돌이에서는 어렵다.

이 문제를 풀기 위해서 우리는 또 다른 종류의 회돌이를 가진다: WHILE 회돌이. 그것은 베이직으로는 다음과 같이 보인다:

J = 1
WHILE J <= 12
    PRINT J, " x 12 = ", J*12
    J = J + 1
WEND

이것은 전과 같은 결과를 산출하지만 for회돌이 대신에 while회돌이를 사용했다. 구조가 while임을 주목하라, 다음에는 불리언 값을(참 또는 거짓이다, 기억나는가?) 평가하는 표현식이 따라온다. 만약 그 표현식이 참이라면 회돌이 안쪽의 코드가 실행된다.

대체용으로 우리는 티클버전을 살펴볼 것이다:

set j  1
while {$j <= 12} {
   puts [format "%d x 12 = %s"  $j [expr $j*12]]
   set j [expr $j + 1]
}

여러분이 보듯이 그 구조는 베이직에서의 WEND 보다는 반괄호 혹은 각괄호와 대단히 유사하다. 그러나 회돌이 안의 복잡한 것들은 무엇인가? 파이썬에서의 형식화 문자열을 기억하는가? format 은 티클의 형식화 문자열이다. $j 는 단지 (문자 'j'가 아니라) j 의 값을 의미하며 expr 는 단지 '다음 부분을 표현식으로 계산하라'고 말해준다. 각괄호는 티클에게 어떤 부분이 먼저 처리되야 하는지를 가르쳐 준다. 티클은 코드를 한번에 모두 번역하여 실행한다는 점에서 평범한 언어는 아니다, 그래서 괄호 없이는 티클은 단어 'expr'를 출력하려고 할 것이고 다음에 더 많은 값들이 있는 것을 보고는 에러메시지를 내고 포기할 것이다 우리는 티클에게 합계를 내고, 다음엔 문자열을 포맷하고, 다음엔 그 결과를 출력하라고 지시할 필요가 있다. 혼동된다고? 걱정하지 마라. 내가 말한바대로 티클은 약간의 독특한 장점들과 많은 기이함을 가지는, 평범한 언어가 아니다.

이제 파이썬을 살펴보자:

>>> j = 1
>>> while j <= 12:
...    print "%d x 12 = %d" % (j, j*12)
...    j = j + 1

지금까지 이것은 매우 직설적으로 보인다. 단지 지적하고자 하는 한가지가 있다면 - 여러분은 위에서 whilefor가 있는 라인의 끝에 콜론(:)이 있는것이 보이는가? 그것은 파이썬에게 한 무더기의 코드(블록 block)가 나타난다는 것을 알려 준다. 대부분의 언어는 블록의 끝을 나타내는 ( 베이직의 WEND나 혹은 티클의 괄호같은) 표식을 가진다. 그러나 파이썬은 들여쓰기를 사용하여 그 구조를 지시한다. 이것이 뜻하는 바는 회돌이 안의 라인들 모두가 같은 양으로 들여쓰기 되어야 하는 것이 중요함을 의미한다. 이것은 읽기에 더 쉬우므로 어쨌든 좋은 습관이다!

더욱 유연한 회돌이

이 섹션의 처음에 사용했던 12단 곱셈표로 돌아와 보면 우리가 만든 회돌이는 12단 곱셈표를 출력하기에 아주 적당하다. 그러나 다른 값들은 어떨까? 여러분은 그 회돌이를 수정하여서 예를 들어 7단 곱셈표도 가능하게 할 수 있는가? 그것은 다음과 같이 보일 것이다:

>>> for j in range(1,13):
...    print "%d x 7 = %d" % (j,j*7)

이제 이것은 우리가 12를 7로 두번 변경해야만 한다는 것을 의미한다. 만약 우리가 다른 값을 원한다면 우리는 그것을 또 다시 변경해야만 한다. 우리가 원하는 곱셈값을 입력할 수 있다면 더욱 좋지 않을까?

우리는 출력될 문자열에 있는 값들을 다른 변수로 바꿈으로써 그것을 할 수 있다. 그러면 그 변수를 우리가 회돌이를 실행하기 전에 설정하라:

>>> multiplier = 12
>>> for j in range(1,13):
...    print "%d x %d = %d" % (j, multiplier, j*multiplier)

이것은 우리의 오랜 친구 12단 곱셈표이다. 그러나 이제 7단으로 바꾸기 위해서, 우리는 단지 'multiplier'의 값을 바꾸기만 하면된다.

여기에 우리는 연속열과 회돌이를 결합하였다. multiplier = 12의 뒤에, 연속적으로in sequence for 회돌이가 따르고, 처음으로 우리는 한개의 명령어를 가진다.

회돌이를 회돌이하기

이전의 예제를 한단계 더 진전시켜 보자. 우리가 (1 은 괴롭히기에는 너무 시시하므로) 2단 부터 12단까지 모든 곱셈표를 출력하기를 원한다고 가정해 보라. 우리가 실제로 해야할 모든 것은 multiplier변수를 회돌이의 부분으로 설정하는 것이다, 다음과 같이:

>>> for multiplier in range(2,13):
...    for j in range(1,13):
...       print "%d x %d = %d" % (j,multiplier,j*multiplier)

처음의 for회돌이 안쪽으로 들여쓰기된 부분은 우리가 처음에 함께 다루기 시작했던 회돌이와 똑 같다. 그것은 다음과 같이 작동한다:
우리는 multiplier를 처음의 값(2)으로 설정한다 그리고는 다음의 회돌이를 돈다.
그리고 나서 우리는 multiplier를 다음의 값(3)으로 설정하고 안쪽의 회돌이를 또 다시 돈다, 등등.
이러한 테크닉은 내포nesting된 회돌이라고 알려져 있다.

한가지 문제점은 모든 테이블이 함께 병합된다는 것인데, 우리는 처음 회돌이의 마지막에 분리라인을 단지 출력함으로써 그것을 수정할 수 있다, 다음과 같이:

>>> for multiplier in range(2,13):
...    for j in range(1,13):
...       print "%d x %d = %d" % (j,multiplier,j*multiplier)
...    print "------------------- "

두번째 print서술문은 두번째의 'for'라인까지 올라와 있는것을 주목하라, 그것은 회돌이 연속에서 두번째 서술문이다. 기억하라, 들여쓰기 수준은 파이썬에서 대단히 중요하다.

어떤 테이블이 따라 오는지 보여주기 위하여 그 분리자를 가지고 실험해보라, 제목을 제공하면 더욱 효과적이다. 힌트 : 여러분은 아마도 multiplier 변수와 형식화 문자열을 사용할 필요가 있을 것이다.

다른 회돌이들

어떤 언어들은 더욱 많은 회돌이 구조를 제공하지만 forwhile 같은 것은 항상 제공된다. (모듈라 2와 오베론은 for 회돌이를 흉내낼수 있으므로 while 회돌이만을 제공한다 - 위에서 우리가 본 바와 같이) 여러분이 마주칠지도 모를 다른 회돌이들은 다음과 같다:

do-while
while과 같지만 점검은 후미에서 이루어지므로 회돌이는 항상 적어도 한번은 먼저 실행된다.

repeat-until
위의 것과 비슷하지만 점검의 논리는 반대이다

GOTO, JUMP, LOOP 등등
오래된 언어에서 주로 보인다, 이것들은 보통 코드에 표식자를 설정하고 명시적으로 직접 그곳에 점프한다.

기억해야할 요점
  • FOR 회돌이는 명령의 집합을 고정된 반복의 횟수 만큼 반복한다.
  • WHILE 회돌이는 명령의 집합을 어떤 종료 조건이 만족될 때까지 반복한다. 만약 종료 조건이 거짓으로 시작된다면 회돌이의 몸체body는 실행되지 않을 것이다.
  • 다른 종류의 회돌이도 존재하지만 FORWHILE 는 거의 항상 제공된다.
  • 파이썬의 for 회돌이는 실제로는 foreach 회돌이이다 - 그것들은 항목으로 이루어진 리스트에 작동한다.
  • 회돌이는 하나의 안쪽에 다시 내포가능하다.
Previous  Next  Contents


이 웹페이지에 대하여 질문이나 제안사항이 있으면 다음 주소로 나에게 전자메일을 보내라: agauld@crosswinds.net