컴퓨터/자질구레 팁

몇 가지 유용한 함수들 만들어 사용하기

adnoctum 2011. 5. 6. 00:41



   프로그래밍을 하다 보면 몇 가지 자주 사용하는 기능들이 있게 되는데, 이런 것은 알면 자주 사용하게 되지만 모르는 경우 지리한 반복 작성을 하게 되곤 한다. 또한, 다소 추상화(일반화)를 해 놓으면 자주 사용할 수 있지만 그런 생각 없이 작성하는 코드에 의존하는 방식으로 (context-specific) 구성하게 되면 일반화 하여 다음에 편하게 사용할 수 있다는 것을 모르는 경우도 있다. 이처럼 다소 일반화 시켜서 사용할 수도 있는 몇 가지 코드의 예를 보자.

   우선, '이렇게 일반화 할 수 있었군', 하는 것을 아는 방법 중의 하나는 다른 언어나 다른 사람이 작성해 놓은 코드를 보는 것이다. 특히 python의 numpy 나 scipy, dephi, matlab 처럼 온갖 자질구레한 것까지 편리하게 만들어 놓은 것들. STL 은 이미 그 자체로 매우 추상화되어 있으니 다소 예외[각주:1].

(이건 어디까지나 예다. 나보다 더 잘, 많이, 멋지게 사용하고 있는 사람들도 많을 것이다)

1. transpose
2. split
3. replace
4. print_vector
5. get file name list (python 의 glob)
6. zip
7. is_file_exist
8. load_item_list




1. transpose
   말 그대로 transpose 를 시키는 함수이다. 2차원 배열에 대하여 정의되기 때문에 parameter 는 단순하다. prototype 은 다음과 같다.

template<class T>

bool transpose(const std::vector<std::vector<T> >& src, std::vector<std::vector<T> >* dest);


template<class T>

bool transpose(std::vector<std::vector<T> >* src);


첫 번째 형태는 transpose 된 결과를 다른 벡터에 반환을 하고, 두 번째 형태는 주어진 형태를 transpose 해주는 형식이다. 개인적으로 parameter 로 넘겨 준 값이 변경된다는 가정이 있는 경우에는 pointer 로 받고, 그렇지 않을 땐 call by reference 로 하기 때문에 위와 같은 의미가 더욱 명확해 진다[각주:2]. 사실 이 기능은 별도의 프로그램으로 만들어 사용하고 있는데, 15,000-by-15,000 matrix 처럼 큰 matrix 를 transpose 하려 하자... memory 가 뻑이 났었기 때문이다. 역시나, scalable 한 알고리즘은 항상 2%의 짱구굴림을 요구한다니까. -ㅋ.

2. split
    말할 필요도 없이 매우 유용한 함수. 주어진 문자열을 token 을 중심으로 해서 잘라 주는 것. strtok를 쓰거나 boost 에 있는 split 을 써도 되겠지만 내 경우엔 이 함수를 직접 만들어 사용한다. 약간 일반화시켜 놓아서 복잡하므로 내용은 넘어 간다.

3. replace
    STL 의 replace 는 '주어진 문자'를 '다른 문자'로 변경하는 replace 가 없는 듯 하다. 그래서 아예 만들어 버렸다.

std::string replace(const std::string& str, char from, char to);


형태는 역시 단순하다.

4. print_vector
   visual studio 의 경우 2010 버전에서는 debuggin 할 때 IDE에서 각 요소를 다 볼 수 있기 때문에 아주 편리한데, terminal에서 gcc로 할 때는 이것이 참 어렵다. 그래서 다음과 같이 만들어 사용한다.


template<class T>

void print_vector(std::ostream* out, const std::vector<T>& v, bool line_by_line)

template<class T>

void print_vector2D(std::ostream *out, const std::vector<std::vector<T> >& v)



만약 terminal 로 뿌리고 싶으면 std::cout 을, file 에 쓰고 싶으면 std::ofstream 에 대한 pointer 를 넘기면 된다. 아니면 std::stringstream을 쓰거나. std::cout 과 std::ofstream 은 모두 std::ostream 에서 상속된 애들이기 때문에 std::ofstream* 는 이 둘을 모두 가리킬 수 있고, 따라서 이처럼 편리하게 사용할 수 있게 된다. 이것이 바로, '부모 클래스의 포인터는 자식 포인터를 가리킬 수 있다', 는 것이 되고, 자식 class 에서 operator << 를 overriding 한 것이 되겠지.


5. get file name list (python 의 glob)
    이것은 말 그대로 file list 를 가져오는 것인데, python 의 glob 과 정확히 같고, windows API 로는 FindFirstFile, FindNextFile 을 이용해서 할 수 있을 것이다. 물론 윈도우즈 버전도 만들어 사용하고는 있다(그 함수). 다음은 linux 버전.

bool get_file_list(const std::string& init_dir, const std::string& filter, std::vector<std::string>* dest)



물론 위 코드는 dirent.h 를 include 해야 한다. wild card 처리가 약간 미흡하긴 하다. -.-


6. zip
    두 개의 list 에서 각 i 번째 요소를 하나의 tuple (std::pair) 로 만들어서 vector 로 반환해 주는 것. 말로 하면 약간 애매한데, 다음과 같다.

 

template<class T1, class T2>

std::vector<std::pair<T1,T2> > zip(const std::vector<T1>& op1, const std::vector<T2>& op2)


명확하다. 요건 얼마 전 python 의 numpy 였나, 그걸 보다가 알게 된 것인데, 왜 여태까지 이렇게 편리한 함수를 만들 생각을 못하고 계속 for 문을 돌렸었는지, ㅋㅋㅋ.


7. is_file_exist
    주어진 파일이 존재하는지 안하는지를 반환하는 것. python 의 os.path.exists 함수나, API의 PathFileExists 함수와 같은 기능. linux 에서 아마도 함수가 있을 것 같은데, 잘 모르겠어서 그냥 std::ifstream 으로 파일 열어서 std::ifstream::is_open 결과를 반환하게 해 놓았다.


8. load_item_list
    file 에서 item 을 읽어 들이는 함수.

bool load_item_list(const std::string& file_name, std::set<std::string>* dest, int IDlen = -1, int col_idx = 0);


default parameter 를 잘 사용하진 않는데, 위는 사용했다. 주어진 file 을 tab-delimited 라 가정하고, [col_idx] 번째 열의 [IDLen] 길이의 문자열을 반환 받는 함수.



   그 이외에도 자질구레한 것이 많은데, 그러한 것들은 생략한다. 어려운 코드라기보다는 '이렇게 간단하게 만들어서 계속 써먹을 수 있는 것'을 보여주고자 한 것이니까.




  1. 실은 이것이 진정한 STL의 힘!! [본문으로]
  2. 그래서 call by reference 인데 함수 안에서 값을 변경하는 경우는 내 경우에는 '없다'. [본문으로]