Beginning Bioinformatics for Biologists
  프로그래밍 시작하기
  Writer : Seyeon Weon   Updated : 10-26   Hit : 5838   Updates 
일단 자신감부터 가지는 것이 가장 중요합니다. 생물 분야의 일들에서 필요한 perl 프로그래밍은 결코 어려운 것도 복잡한 것도 아닙니다. 물론 perl script는 복잡해질 수 있는 정도에 한계가 있는 것이 아니니, 얼마든지 복잡한 것을 만드는 것이 가능합니다. 하지만 우리 일의 대부분은 이런 복잡한 것들이 아니고, 기본적인 것들만 알면 큰 힘 들이지 않고 해낼 수 있는 것들입니다. 문제는 용기를 가지고 시작을 하는 것입니다. 그리고는 조금 정성과 시간을 들여서 공부를 해내는 것입니다. 몇 달 정도 고생을 해서 이렇게 한 번 해두면, 평생토록 자신의 가치를 높여주는 기술이 될 것입니다.
 
프로그래밍이란 컴퓨터가 알아보도록 어떤 일의 수행에 필요한 일련의 명령들을 적어주는 것입니다. 컴퓨터는 시키는 대로만 합니다. 오늘날 우리가 사용하는 컴퓨터들은 폰 노이만 방식 컴퓨터라고 해서 기본적으로 같은 모양을 가지고 있습니다. 컴퓨터에서 실제로 계산이 수행되는 부분이 CPU라는 것을 모르는 사람은 없을 터이고, 오늘날 우리가 사용하는 CPU 하나에는 거의 1억 개에 달하는 트랜지스터들이 연결되어 정해진 여러 가지 기능들을 수행하도록 꾸며져 있습니다. CPU의 작동은 물론 고정적인 것이 아니고, 우리가 넣어준 input에 따라서 output이 달라지도록 작동을 합니다. 그리고 오늘날 우리가 사용하는 디지털 컴퓨터는 동일한 input에 대해서는 항상 동일한 output이 나오도록 만들어져 있습니다. (이를 "deterministic하다"라고 합니다.)
 
CPU 상의 어떤 기능이 작동을 하도록 만들기 위해서는, 이진수로 된 소위 기계어라는 것을 입력을 해주게 됩니다. 이 기계어가 실제로 CPU가 이해를 하는 명령입니다. 그렇다면 프로그래밍 언어란 무엇일까요? 오늘날 우리가 사용하고 있는 컴퓨터의 원안을 만들어낸 폰 노이만은 계산의 천재로도 유명한데, 다른 사람들이 기계어로 프로그래밍을 해내지 못하는 것을 의아해 했다고 하는 이야기도 전해집니다. 우리 같은 보통 사람들은 물론 기계어보다는 훨씬 인간적인 언어가 필요합니다. 이 둘 사이의 간격을 메우기 위해서는 이렇게 하면 되겠죠. 문법과 사용할 수 있는 단어 등이 정확하게 정해진 언어가 있어서, 이 언어의 규정에 따라서 사람이 적어주면, 그것을 다시 기계어로 번역을 해주면 되겠죠. 이러한 번역을 해주는 소프트웨어를 compiler라고 합니다. perl의 경우에는 interpreter라고 하는데, 사실상 같은 것이라고 보면 됩니다. compiler에서는 기계어 코드로 된 파일이 따로 만들어지고, 이 파일을 다시 실행(즉, CPU에게 보내는 것)을 하는 것이고, interpreter에서는 사람이 짠 코드가 따로 파일로 만들어지지 않고 메모리 상에서만 번역이 되어서 곧장 실행이 됩니다. 그리고, 사람이 짠 코드를 소스 코드라고 한다는 것 정도는 이미 다들 알고 있을 터이고요.
 
이러한 프로그래밍 언어들이 가지는 가장 기본적인 특징은, 사람이 문법을 어기지 않고 짰을 경우에는 애매한 점이 있다거나 혼동이 된다거나 하는 문제가 전혀 없이 compiler가 완벽하게 분석을 해서 번역을 할 수가 있으며, 그 번역 속도 또한 매우 빠르다는 점입니다. (이러한 성질은 이들 문법이 전산학 용어로 context free grammar라고 하는 class에 속하기 때문입니다.) 그리고 이러한 번역 잡업을 할 때에 compiler가 하는 중요한 일 중의 하나로, 주어진 소스 코드에서 최대한 실행 속도가 빠른 기계어 번역이 나올 수 있도록 여러 가지 복잡한 변형들을 가하게 되는데, 이를 "optimization을 한다"라고 합니다. 물론 소스 코드를 짠 사람의 의도에서 벗어나게는 하지 않으며, 예를 들어 순서의 재배치 등과 같이 실제로 수행되는 일에는 변화가 없이 실행 속도만 더 빠르도록 해주는 것입니다.
 
그 다음 library에 대한 이해가 필요한데, 우리의 경우에는 BioPerl이란 것이 가장 중요한 library입니다. library에 대한 설명은 이곳에서 반복은 하지 않으니, "BioPerl이란"에서 반드시 읽어보기를 바랍니다. 현실세계에서의 대부분의 프로그래밍 작업들은 필요한 library들을 불러서 자신이 원하는 목적에 맞도록 짜깁기를 하는 것입니다. 따라서 compiler가 해야 하는 일 중의 하나는, 우리가 짠 소스 코드를 기계어로 번역하는 일과 동시에, 우리가 짠 소스 코드 속에서 사용이 된 library들을 찾아서 연결을 시키는 일입니다. 이를 "link를 한다"라고 합니다. 지금 적은 것은 더 일반적인 언어들에서 작동하는 방식이고, perl의 경우에는 우선 library라는 용어보다는 module이란 용어를 더 일반적으로 사용한다는 점이 있으며, module의 소스 코드를 불러서 우리가 짠 소스 코드 속에 고스란히 삽입을 하는 방식으로 작동을 하는 것이지만, 외형적인 효과는 동일한 것이 됩니다.
 
만약 perl interpreter와 perl module들이 자신의 컴퓨터 속에 마련이 되어있다면, 이제 소스 코드를 작성할 때 사용할 문서 편집기가 필요하겠죠? (perl interpreter와 perl module의 설치에 대한 내용은 다른 글에서 적겠습니다.) 문서 편집기에 대한 내용은 "문서 편집기 사용하기"에서 읽어보기 바랍니다. 이제 필요한 것은 다 갖춘 셈이로군요. 그리고는 이 모든 것은 다 유닉스(리눅스 또는 cygwin) 환경에서 수행하는 것을 기본으로 하고 있다는 점은 잘 알고 있죠? 더 구체적으로는 bash라는 shell이 제공해주는 환경이 되는 것이고요. (아직 적지 않았지만, 이 부분에 대해서는 다른 글에서 설명이 됩니다.)
 
이제 모든 준비가 되었으니, 문서 편집기를 띄우고 perl script들을 만들어보도록 합시다.
 
아래 내용을 담은 plain text 파일을 만들어서, 자신이 원하는 파일명으로 저장을 해내지 못하는 사람이 이 글을 여기까지 읽고 있는 불상사야 설마 생길 리가 없을 것 같군요.
 
print "I feel great!";
 
이렇게 하면 안 좋다는 것을 강조하는 의미에서 t1.pl이란 파일명으로 저장을 했다고 합시다. ^^; 우선, .pl은 perl script 파일의 확장자로 일반적으로 사용되는 것입니다. 물론 다른 확장자를 사용해도 되지만, 강제되는 규정은 아니지만 모두가 지키고 있는 약속과 같은 것입니다. t1.pl, t2.pl, t3.pl 같은 식으로 해두면 나중에 황당하게 되겠죠. 파일명으로는 어느 파일이 어떤 일을 하도록 짜놓았는지 짐작도 안 될 터이니... 따라서, 지나치게 길지는 않는 정도로 영어 단어들을 제대로 사용해서 파일명을 만드는 것은 당연히 해야 하는 일입니다.
 
print는 함수명이고, 따옴표 안에 있는 것은 이 함수에 input으로 들어갈 부분입니다. 따옴표 자체는 input에 포함되지 않습니다. 끝에 있는 ;은 여기까지가 구분되는 단위임을 알려주는 일종의 마침표와 같은 역할을 하는 것입니다. 이 세미콜론을 잊어버리고 빠뜨리면 안 됩니다. (이 경우에는 사실 없어도 실행이 되는데, 이것은 한 줄 짜리라서 그런 것입니다. 이런 경우는 실제 상황에서는 없는 일이겠군요.)
 
그 다음에는 bash prompt 상태에서,
 
perl t1.pl
 
이라고 입력하고 엔터키를 쳐줍니다. 그럼 화면에
 
I feel great!
 
라고 나오게 되죠. 만약 한 라인 대신에 아래와 같이 두 라인을 입력을 해주면 어떻게 될까요?
 
print "I feel great!";
print "I feel really great!";
 
예상밖으로 다음과 같이 나오게 됩니다.
 
I feel great!I feel really great!
 
상당히 실망스럽죠? 우리의 의도를 파악해서 다음 줄로 가는 것까지는 못 할지라도, 심지어는 띄어쓰기조차 해주지 못하다니 말입니다. 컴퓨터는 완전히 우리가 시키는 대로만 합니다. 위 perl script 어느 곳에도 다음 줄로 가라거나 한 칸을 띄우라는 명령이 없습니다. 컴퓨터는 시키는 그대로를 한 것뿐입니다.
 
print "I feel great!\n";
print "I feel really great!";
 
처럼 입력을 해주면 우리가 원하는 대로 두 줄에 제대로 인쇄가 됩니다. "\n"이 바로 이를 해주게 하는 부분입니다. 우리가 원하는 것은 다음 줄로 가라는 것, 즉 엔터키에 해당하는 것인데, 엔터키를 편집기에서 직접 입력을 해가지고서는 원하는 바를 달성할 수가 없다는 심각한 문제가 있다는 것은 굳이 설명할 필요가 없겠죠? 엔터키와 같은 문제를 가지는 것들이 좍 있는데, 이들은 모두 "\ + alphabet" 형태로 입력을 해서 해결을 해주게 됩니다. 그럼 \를 입력하고 싶으면 어떻게 하느냐고요? 이때는 "\\"라고 입력을 해주면 됩니다. 간단하죠?
 
그럼
 
print "I feel great!\n";print "I feel really great!";
 
와 같이 하면 어떻게 될까요? 위와 완전히 동일합니다. perl interpreter는 띄어쓰기나 다음 줄로 가기 등등은 전혀 개의치 않습니다. 그러니까,
 
print "I feel great!\n";         print "I feel really great!";
 
또는, 
 
print "I feel great!\n";
 
 
 
 
               print "I feel really great!";
 
도 완전히 같은 것입니다. 적어도 perl interpreter에게는... 그렇지만 물론 이렇게 엉망으로 적어서는 좀 곤란할 것입니다. 작성한 소스 코드는 작성자 자신도 나중에 봐야 하고, 때로는 다른 사람이 들여다볼 일도 생길 것입니다. 그리고 최초에 짜는 동안에도 빈번하게 이미 짠 부분을 훑어봐 가면서 짜야 하니, 당연히 사람이 읽기 편하도록 잘 정돈을 하는 것도 필요합니다. (이에 대해서는 다른 글에서 더 자세히 적겠습니다.)
 
자, 이제부터가 진짜 프로그래밍입니다.
 
$mood = "I feel great!";
print "$mood\n";
$mood = "I feel really great!";
print $mood;

 
라고 입력을 해주면 어떻게 될지 짐작이 되나요? 물론 위와 똑같은 결과가 나옵니다. 컴퓨터 상의 메모리 공간에다 $mood라는 이름을 붙인 방을 하나 만들어서, 이곳에 "I feel great!"를 저장을 해두고서는, print 함수의 input으로는 $mood를 원래의 문장 대신에 사용한 것입니다. 이처럼 이름이 붙여진 메모리 상의 공간을 변수(variable)라고 합니다. perl에서는 $와 같은 특수 기호가 항상 앞에 붙어있죠. (이에 대해서는 나중에 더 자세히 설명을 합니다.)
 
위의 소스 코스에서 $mood가 재차 사용이 되었죠. 이 경우에 어떤 일이 일어난 것일까요? 원래 있던 "I feel great!"는 사라지고 "I feel really great!"로 대체가 된 것입니다. 즉, 변수는 생수병처럼 재사용이 가능합니다. 물론 제한없는 재사용입니다. 여기까지 이해가 된 것이 너무 기쁜 나머지, "I feel great!"를 100번쯤 화면에 뿌리고 싶으면 어떻게 하면 될까요?
 
$mood = "I feel great!";
print "$mood\n";
print "$mood\n";
print "$mood\n";
print "$mood\n";
print "$mood\n";
print "$mood\n";
..........
 
처럼 좍 적어주면 되겠죠? 그런데 이렇게 하는 것이 효율적이지 못하다는 것은 누구나 느끼는 점일 터이고요.
 
$mood = "I feel great!";
for ($i=0; $i < 100; $i++) {
    print "$mood\n";
}
 
처럼 하는 것이 정답입니다. 100번 반복을 해주는 기능을 수행하는 부분인, "for ($i=0; $i < 100; $i++) { }"이 구체적으로 어떻게 작동을 하는 것인지 이해를 해보도록 합시다.
 
우선 {}괄호에 대해서 설명을 해야 하는군요. {}는 그 안에 있는 것을 하나의 묶음으로 만들어주는 것입니다. 그러니까, {}안에 있는 것이 100번 실행이 되는 것이죠. 지금은 한 줄밖에 없지만, 얼마든지 여러 줄을 적어줘도 됩니다. 게다가 다른 {}로 묶은 것이 들어가 있어도 됩니다. 단지 여는 괄호와 닫는 괄호의 짝을 맞추어주어야 하는 것이죠. 우리가 잘 알고 있는 수학에서의 수식과 같은 식입니다. 제대로 짝을 맞추어서 열고 닫기만 한다면 괄호의 수는 상관이 없는 것이죠.
 
$i가 변수라는 것이 이해가 되죠? 최초의 프로그래밍 언어인 FORTRAN에서는 I(그 당시에는 대소문자 구분을 하지 않고 대문자로만 프로그래밍을 했었습니다.)부터 N까지의 알파벳으로 시작되는 변수명을 가진 변수들에는 정수를 담고, 나머지 알파벳으로 시작되는 변수명을 가진 변수들에는 실수를 담았었습니다. 그럴 필요가 전혀 없는 오늘날의 프로그래밍 언어들에서도 여전히 정수를 담는 변수명에 $i와 같은 식으로 흔히들 쓰고 있는 것은, 이러한 수십 년 묵은 역사 때문입니다. 그리고 물론 정수가 영어로 integer인 것에서 i가 유래한 것이고요. 저는 82년도에 FORTRAN으로 처음 컴퓨터 프로그래밍을 배웠었는데, 저같은 사람들이 쓴 책과 짠 소스 코드들의 영향이 이어져오는 것이겠군요.
 
일반적으로 프로그래밍 언어들은 이처럼 변수가 담고 있는 값이 어떤 타입의 것인지를 구분을 해주어야 합니다. perl의 경우에는 특이한 해결책을 사용합니다. 굳이 사람이 일일이 구분을 해주지 않더라도, 그 변수에 넣어주는 값의 생긴 모양을 보면 100% 어떤 타입인지 알아낼 수가 있다는 점을 이용합니다. 즉, perl에서는 사람이 일일이 지정을 해주는 대신에, 넣어주는 값을 엉뚱하게만 하지 않으면 됩니다. 예를 들어 위에서, loop 변수(이처럼 {} 안의 것을 여러번 실행하도록 하는 것을 loop이라고 합니다. 그리고 loop의 실행 횟수를 count하는 용도로 사용되는 변수를 loop 변수라고 합니다.)는 당연히 정수라야만 할 것입니다. 그런데, "$i = 1.1"처럼 정수가 아닌 수로 해주는 멍청한 사람은 없을 것입니다. 이런 사람은 프로그래밍을 하는 기본 자세가 안 되어 있는 것이겠군요. 즉, "$i = 1"이라고 해주면 perl interpreter는 이것이 정수를 담고 있는 변수임을 우리가 의도하는 바대로 알아먹게 됩니다.
 
"for ($i=0; $i < 100; $i++) { }"에서 ()안에 세미콜론으로 나누어진 3부분이 있는 것이 보이죠? loop 변수인 $i를 0부터 시작해서 하나씩 증가를 하면서 100보다는 적을 동안에 {}안의 내용을 실행하라는 뜻입니다. $i++은 "$i = $i + 1"를 줄여서 쓴 것입니다. 워낙 자주 쓰게 되는 것이라서 이처럼 줄여서 쓰는 표기법을 만들어둔 것입니다. 그리고, 위에서 이미 설명한 것과 같이 변수는 얼마든지 재사용이 가능합니다. 따라서, "$i = $i + 1"도 전혀 문제가 없는 것입니다. 원래 $i에 15가 들어 있었다면, "$i = $i + 1"를 실행하면 $i에 16이 들어가 있게 되는 것이죠.
 
그런데 왜 0부터 시작을 하느냐고요? 대다수의 프로그래밍 언어들은 이처럼 0부터 시작을 해서 count을 합니다. 3개까지 세는 방법은 하나, 둘, 셋이 아니라, 영, 일, 이인 것이죠. 즉, 무언가가 3개가 있으면, "2"만큼 있다고 합니다. 좀 황당하게 느껴지죠? 그런데 60년대에 만들어진 C 언어 이후에 만들어진 거의 모든 프로그래밍 언어들에서는 이처럼 count의 시작을 0부터 합니다. 사실 그 이유를 지금 제대로 적기에는 꽤 복잡한 이야기입니다. 일단은 여러 모로 편리한 점이 많아서 그렇다는 것만 이야기를 해둡니다. 사람의 느낌에는 조금 불편하지만, 이것만 극복이 된다면 다른 더 편리한 점들이 있는 것이죠. http://en.wikipedia.org/wiki/0_(number)에서 "7 In computer science"라고 되어 있는 항목에 제대로 된 설명이 있으니, 한 번 읽어보기 바랍니다. 그리고 무슨 소린지 지금은 이해가 잘 되지 않더라도 너무 걱정은 하지 말기 바랍니다. 머지않아 0부터 시작하는 것에 어느 새 익숙해져 있는 자신을 발견할 수 있을 것입니다.
 
물론 위의 예에서는, 예를 들어 "for ($i=232343; $i < 232443; $i++) { }"처럼 해주어도 완전히 같은 결과가 나옵니다. 그런데 이렇게 하는 것 역시 프로그래밍 하는 기본 자세가 안 되어 있는 것이겠죠. 특별한 이유가 없으면 당연히 0부터 시작을 해야 합니다. 이것 또한 누가 강제하는 것은 아니지만, 그냥 그렇게 하는 것이 정상이라서 그렇게 하는 것입니다.
 
더 설명할 것은 이젠 없죠? 변수와 loop을 배웠으니, 이젠 컴퓨터 프로그래밍에 대해서는 다 배운 셈입니다. 물론 농담입니다. ^^; 하지만, 핵심은 다 된 것입니다. "아니, 이렇게 쉬운 것을 못 배워서 그토록 난리였단 말인가?"라는 생각을 하는 사람은 적절한 생각을 하는 것입니다. 실제로 이처럼 쉬운 것입니다. 그야말로 아무 것도 아닌 것이죠. 이런 걸 못 배워서 다들 불가능한 일로만 여기는지 도무지 이해가 안 됩니다! ^^;
 
이제 컴퓨터 프로그래밍을 얼마든지 해낼 수 있다는 자신감이 생겼나요? 우리 분야의 일들을 위해서는 지금까지 공부한 것을 토대로 조금 살을 붙여나가면 됩니다. 다음 글에서 살을 붙이는 과정을 계속 해보도록 합시다.

  06-10-24
perl과 같은 interpreter 언어인 경우에는 소스 코드를 흔히 script라고 부릅니다.
  06-10-25
위에 폰 노이만이 다른 사람들은 왜 자기처럼 기계어로 직접 짜질 못하고, 굳이 high level 언어를 만들어야 하는지 의아해 했다던 프로그래밍 언어가 바로 이 포트란입니다. 여기에서 high level 언어라고 부르는 까닭은 이해가 되죠? 기계어가 low level 언어인 것입니다.
  06-10-25
실제로는 이 두 가지가 완전히 동일하지는 않습니다. 그러나 현재 우리의 논의에서는 완전히 같은 것이라 생각을 해도 됩니다.
Up