LiveScript 살펴보기 – 04 제어문

이번 글에서는 LS의 기본적인 제어 구문인 반복문과 분기문을 작성하는 방법에 대해서 살펴볼 예정이다. LS의 특징인 “거의 모든 것은 표현식이다”라는 점에서 for/while 문 자체도 하나의 표현식으로 최종 표현식의 결과를 모은 리스트로 평가될 수 있다는 점을 놓치지 말자. LiveScript 살펴보기 – 04 제어문 더보기

(Dos) 명령줄에서 For 사용하기

나는 vim을 소스에서 mingw-w64로 컴파일하여 사용중이다. vim을 컴파일한 결과물은 소스 디렉토리에 생성된다. 이 파일을 원래 vim 이 있던 자리로 복사하는 과정은 꽤나 귀찮을 수 있다. 물론 원래 vim.exe 파일이 있던 자리에만 복사하면 되지 그게 무슨 문제냐…고 할 수 있지만 Git 디렉토리 내에도 vim 이 있고 msys 내에도 vim 이 있기 때문에 좀 곤란하다. 한방에 할 수 있는 방법이 없을까?

물론 수동으로 해당 위치들을 파이썬 스크립트 같은 곳에 지정해놓고 복사하는 코드를 짜는 것도 방법이겠다.

from pathlib import Path
import subprocess
from shutil import copyfile

def main():
    paths = [Path(x).parent for x in subprocess.check_output(['where', 'vim'], 
        universal_newlines=True).split('\n')][1:]

    exes = ('vim.exe', 'gvim.exe')
    for e in exes:
        if (Path(__file__).parent / e).exists():
            print("FILE {} IS CHECKED, START COPYING".format(e))
            for p in paths:
                if p == Path(__file__).parent:
                    continue
                try:
                    print("  COPYING {} TO {}".format(e, str(p)))
                    copyfile(str(e), str(p / e))
                except:
                    print("    ERROR DURING COPYING {} TO {}".format(e, str(p)))

if __name__ == '__main__':
    main()

근데 이건 너무 귀찮으니까 (아니, 이 시점에 이미 다 짜두었는데?) 다음과 같이 도스의 FOR 명령을 쓸 수 있다. 사실 DOS 역시 FOR 문을 지원하고 있는데, 보통 쉘에서 반복문은 한 번 알아두면 요긴하게 쓸 데가 많긴하다.

FOR /F "skip=1 delims=" %G IN ('where vim') DO COPY /Y vim.exe"%~dpG"

몇 가지 설명을 곁들여 보자. 자세한 내용은 명령 프롬프트 상에서 HELP FOR를 입력해서 볼 수 있다.

  1. /F 옵션은 파일(집합) 및 문자열이나 명령의 결과의 각 행에 대해서 특정 명령을 반복합니다.
  2. /F 뒤에는 옵션이 옵니다. 각 옵션은 따옴표 안에 들어가며 공백으로 구분합니다.
  3. skip=1은 첫번째 행을 무시한다는 의미입니다. (왜냐면 첫번째 행은 현재 디렉토리가 될거기 때문에 파일을 자기 자신으로 복사할 수 없습니다.)
  4. delims=는 각 행의 기본 구분값(공백이나 탭)을 무시한다는 의미입니다. 엄청 귀찮게도 "Program Files (x86)"과 같이 공백이 들어간 디렉토리명을 시스템이 강제하는 병신같은 윈도우….
  5. %G는 각 행을 받을 변수입니다. 만약 이 명령이 배치파일 내에서 사용된다면 %%G이라고 두번 써야 합니다.
  6. IN 다음에는 집합이 옵니다. 여기서는 ('where vim')이라고 %PATH%에 포함된 vim 실행파일 경로를 찾습니다.
  7. DO 다음에는 실행할 명령입니다.
  8. %~dpG%G를 확장합니다. 드라이브이름과 경로로 확장한다는 의미입니다. 이 때 디렉토리 안에 공백이 들어간 병신같은 시스템이므로 이를 따옴표로 둘러싸야 한 덩어리의 경로로 인식됨에 주의합니다.

따라서 최종적으로 생성한 deplay.bat 의 내용은 다음과 같다.

@echo off
FOR /F "skip=1 delims=" %%G IN ('where vim') DO COPY /Y vim.exe "%%~dpG"
FOR /F "skip=1 delims=" %%G IN ('where vim') DO COPY /Y gvim.exe "%%~dpG"
echo on

[Python101] 로직(2) – 반복문

지난 시간에는 조건을 통해 갈림길 중 한쪽으로 진로를 결정하는 조건문에 대해 살펴 보았다. 이번 시간에는 반복문에 대해 알아보도록 하겠다.

반복문은 쉽게 말해서 같은 (혹은 비슷한) 작업을 여러 차례 반복하도록 지시하는 구문이다. 파이썬의 반복문은 조금 독특한 구석이 있는데, 이는 설명을 하면서 이야기하도록 하겠다.

while 문

중고등학교 때로 기억을 거슬러 올라가보면 (요즘은 어떤지 모르지만) 그 때는 컴퓨터 시간에 BASIC을 배웠는데, 이 때도 FOR 문이라는 게 있었다. 그 이후로 반복문의 대명사는 사실 for 인데, 파이썬의 for는 약간 다른 (그리고 좀 멋진) 부분이 있어서 먼저 while 문을 살펴보도록 하겠다.

while 문은 어떤 조건이 있고, 이 조건이 참인 동안에는 지정된 명령을 계속해서 반복하도록 하는 구문이다. while 문은 다음과 같이 쓴다.

while 조건식:
    반복하는 명령

파이썬 인터프리터가 조건식을 만나면 조건식을 판별한다. 조건식의 결과가 참인 경우에는 반복 명령을 한 번 수행한 후 다시 조건식을 판별한다 결과가 참인 경우에는 이를 반복하고 거짓인 경우에는 더 이상 다음 명령을 수행하지 않고 그 다음 블럭으로 건너뛰게 된다.

다음은 가장 간단한 while 문의 예라 하겠다.

x = 1
while x =<40:
    print x
    x = x + 1

이 소스를 따라가보자. 먼저 x 에는 1이라는 값이 대입된다. 그런 다음 반복문을 만난다. x 는 40보다 작으므로, 3, 4 번 라인이 실행된다. 1이 출력되고 x 에는 x + 1 값(= 2)이 대입된다. 조건 식은 여전히 참이므로 계속해서 값이 출력된다. 40번째 반복에서 x = 41이 되고 더 이상 조건식을 만족하지 않으므로 반복구간 (반복 구간은 흔히 ‘루프’라고 부른다.)은 종료된다.

반복문의 중첩

반복문의 블럭 안에는 다른 반복문이 들어가도 된다. (물론 조건문이 들어갈 수도 있다.) 당연한 이야기를 써 놓으니 이상한데, 구구단을 출력하는 프로그램을 생각해보자

각 단은 *2, *3 … *9 한 값을 출력한다. 이는 반복문을 통해서 처리할 수 있다. 그리고 각 단은 2단 부터 9단까지 반복된다. 역시 반복문을 통해 처리한다. 즉 구구단 프로그램은 다음과 같이 구상할 수 있다.

  1. while 문을 통해 각 단의 값에 2~9를 곱해서 출력하도록 할 수 있다.
  2. 1의 작업을 다시 2~9단에 걸쳐서 반복한다.

실제 소스 코드는 다음과 같다. ex14.py는 while 문을 사용해서 구구단을 출력하는 프로그램이다.

#-*-coding:utf-8
#ex14.py
i = 2 # 각 단의 번호
while i < 10j = 2 # 각 행의 번호, 새로운 단을 시작할 때 2로 초기화한다.
    while j < 10:
         print "%d * %d = %d" % (i, j, i*j)
         j = j + 1
    print # 각 단이 끝나고 나면 한 줄을 띄운다.
    i = i + 1

소스를 분석해 보자

  1. 4행에서 i는 2단, 3단… 각 단을 나타내는 값이다. 구구단은 2단부터 시작하므로 2로 초기화한다.
  2. while 문을 통해  i 가 10보다 작을 때까지 반복한다. 반복문 안에는 각 단을 출력하는 부분이 들어가면 된다.
  3. 6행에서 다시 j=2로 초기화한다. 이는 각 단에서 매 행마다 곱해질 값이다.
  4. 7행은 while문을 통해 j=2,3,4…,9까지 반복하도록 한다.
  5. 중첩된 while문의 내부에서는 구구단의 각 행을 출력한다. 포매팅 문자열을 사용하여 깔끔하게 처리했다.
  6. 9행에서 한 줄의 문장을 출력한 다음에는 j에 1을 더한 값을 다시 대입한다.
  7. 이런 식으로 7~9행이 반복된다.
  8. 반복이 끝나면 11행을 만나는데 이 때 빈줄을 한 줄 출력한 다음, i에는 다시 1을 더해준다. 2단이 끝났으니 이제 3단을 출력할 차례이다.
  9. 위의 과정을 반복한다. 9단을 출력하고나면 맨 마지막에 i = 9 + 1 이된다. while의 조건을 만족하지 않으므로 반복문이 끝나게 된다.

말로 풀어서 설명해도 사실 명쾌하지는 않다. 대신 소스 코드를 보면서 하나 하나 추적해보면 어떤 식으로 흘러가는지 감이 올 것이다.

for 문

for 문은 조건을 사용하지 않는 반복문이다. (사실 다른 언어의 for 문은 대부분 조건을 사용한다.) for 문은 대신 “집합”을 사용한다. for 문의 사용방법은 다음과 같다.

for 변수 in 집합:
    *변수*를 사용하는 명령

파이썬의 for 문은 특별하다. 즉 어떤 집합에 대해 그 집합의 개개의 원소에 대해 같은 동작을 반복한다. 즉 어떤 조건을 만족하는 동안 반복하는 것이 아니라 집합의 원소에 대해 개별적으로 어떤 동일한 처리를 하고자 할 때 유용하다.

예를 들어서 1에서 500까지의 숫자를 모두 더하는 코드를 작성해보자.

sum = 0
for x in range(1,501):
    sum = sum + x
print sum

range()라는 함수가 처음나왔다. 대화형 쉘에서 help(range)해보면 알 수 있는데, 시작 값과 끝 값 사이의 범위를 “집합”으로 만들어주는 함수이다. 이 때 range() 함수의 끝 값은 실제로 범위에 포함되지 않는다. 따라서 range(1,501)은 [1,2,3,…,500] 까지의 집합을 나타낸다.

다시 for 문을 보자. x in range(1,501) 이라고 하였으니, 1,2,…500까지의 숫자 값이 x가 되어 매번 루프를 반복하게 된다.

다시 코드를 설명하면,

  1. sum은 합계값을 저장하는 변수이다. 0으로 초기화한다.
  2. 1~500 사이에 숫자에 대해 for 문으로 반복한다. 이 값들은 x에 대입된다.
  3. 매번 루프에서 x값을  sum에 더해서 sum에 대입한다. 즉 sum은 점점 값들이 쌓여간다.
  4. 루프를 다 돌고나면 sum을 출력한다.

이해가 좀 되었는지?

이 for 문을 사용하면 구구단 출력 프로그램은 훨씬 간단하게 작성할 수 있다.

for i in range(2,10):
    for j in range(2,10):
        print "%d * %d = %d" % (i, j, i*j)
    print

이제 위 코드를 읽고 무슨 말인지 이해할 수 있을 것이다.

“집합”

사실 파이썬에서는 iterable이라고 표현하는데, 이를 마땅히 번역할만한 마땅한 단어가 없어서 집합이라고 일단 해 두도록 한다. for 문은 이 집합의 각 원소를 돌아가며 반복하는 반복문이다. 파이썬에서 이 “집합”은 상당히 중요하다. for 문 뿐만 아니라 이런 집합들의 원소에 대해 개별적인 처리를 할 수 있는 방법들이 꽤 있으며, 이런 것들을 잘 활용하면 코드의 수를 크게 줄일 수 있다.

우선 반복문에 대한 기초적인 내용을 배웠으니, 다음은 정말 중요하다는 “집합”에 대한 내용을 조금 집중적으로 다뤄보도록 하겠다.