Skip to main content

Blub 레벨 높이기

갑->을이라고 할 때: '갑'는 '을'보다 자신이 더 잘났다고 생각한다.
갑<->을 이라고 할 때: '갑'과 '을'은 둘 다 자신이 더 잘났다고 생각한다.

c0037181_04030621.gif
출처: 프로그래머 서열

.

Beating the Average라는 글에 보면 Blub Paradox라는 말이 있습니다. Blub Paradox를 이해하기 위해서는 먼저 프로그래밍 언어는 모두 가지고 있는 힘이 다르다는 사실을 인정해야 합니다. C언어가 기계어보다 더 강력하듯이 python이나 C++, ruby, java, lisp 등의 고급 언어들 사이에도 이런 서열이 존재한다는 것이죠.

그럼 이제 Blub이라는 가상의 언어를 생각하고 이 언어를 현재 우리가 사용하고 있는 언어라고 합시다. 이 언어가 COBOL보다는 강력한 언어라고 가정하면 서열은 다음과 같습니다.

기계어 < COBOL < Blub < ?


이제 Blub 프로그래머가 아래, 즉 COBOL과 같은 언어를 보면 COBOL이 왜 Blub보다 약한 언어인지를 알 수 있습니다. 자신이 사용하고 있는 Y라는 기능이 COBOL에는 없기 때문이죠. 심지어 어떻게 Y라는 기능도 없는 언어로 프로그래밍을 할 수 있는지 궁금해 합니다.

하지만 반대로 위를 올려다보면 우리는 우리가 보고 있는 것이 무엇인지를 깨닫지 못합니다. 단지 Blub으로도 할 수 있는 일을 이상한 문법이나 방법으로 하고 있다고 생각하기 쉽죠. 결국 그 언어가 왜 Blub보다 강한지 알지 못하는데 이를 Blub Paradox라고 합니다. 이렇게 더 강한 언어의 강력함을 이해하지 못하는 이유는 바로 우리가 현재 Blub의 높이에서만 생각할 수 있기 때문입니다.

위의 내용에서 유추해보면 결국 모든 언어의 장/단점과 차이를 이해하기 위해서는 가장 강력한 언어를 알아야 한다는 결론이 나옵니다. 물론 위에서 언급한 Paul Graham의 글에서는 이 언어가 Lisp이라고 이야기하고 있죠.

얼마전까지 제 Blub은 C++였습니다. 이전엔 python을 그저 C++의 인터프리터 버전으로 간주하고 사용했습니다. 즉, C++로도 할 수 있는 일만 python으로 했던 것이죠. 그런데 지난주에 테스트 자동화 툴을 python으로 작성하다가 실제 테스트 케이스를 작성할 사람들을 위해 자동으로 테스트 케이스가 등록되도록 python의 reflection 기능을 공부하여 사용하게 되었습니다. 이 기능을 사용하다 보니 이런 생각이 들더군요. "reflection 기능도 없는 C++에서 앞으로 이런 작업을 해야 하면 어떻게 하지?"

이를 계기로 제 Blub 레벨이 조금은 올라가지 않았나 생각합니다.

저도 C++이라는 언어에 대해서는 제법 잘 알고 있다고 생각하는데 Lisp이나 Smalltalk같은 언어를 아는 사람을 보면 괜시리 주눅이 들곤 합니다. 아마 저보다 높은 Blub 레벨을 가지고 있어 저보다 넓은 시야를 가지고 있을거라 생각하기 때문이었을 겁니다. 저도 Lisp의 경우엔 몇번 배우기를 시도해 보았는데 매번 더 재밌거나 더 급한 일들이 생겨 실패하곤 했습니다. 올해 다시 한번 Lisp이나 Smalltalk에 도전해서 Blub 레벨을 올려 봐야겠습니다.

그나저나 위의 그림을 보니 Java는 제대로 안습이군요. :-) Java가 제가 별로 좋아하는 언어가 아니라 다행입니다. 아니었다면 상처 받았을 듯.

Comments

  1. 전 template없이 어찌 코드를..

    이란 생각을 할때가 있다는.. 킁;

    ReplyDelete
  2. 저랑 Blub 레벨이 비슷하시군요. :-) template도 그렇지만 전 destructor가 없어서 RAII가 안되는 언어들을 만나면 대략 난감입니다. 보통 그런 언어들이 RAII 대신 사용하는 try~finally 블록은 어딘지 우아하지가 않아 보여요.

    ReplyDelete
  3. 그게.. 신기하게도 실제로 Java 프로그래머들은 이유없이 C#, C, C++ 등의 언어에 대해 열등감을 가진 것 처럼 보입니다.
    반면 Ruby 프로그래머들은 세상에 루비만 있는 줄 알죠.
    잘 작성된 표 같습니다. ㅎㅎ

    ReplyDelete
  4. 음.. 그리고 C++에는 비록 reflection이 없지만, template meta programming이라는 강력한 도구가 있잖아요...

    ReplyDelete
  5. 아마 Ruby 프로그래머들의 자존심은 자신들이 그래도 제일 Lisp의 후계자다라는 생각이 있어서 그런건 아닐까요? Java는 저번에도 한번 썼던 것 같은데 eclipse만 없었더라면 C/C++과 Python, Ruby같은 script 언어들 사이에 끼어서 고생하지 않았을까 싶네요. 언어 자체에 문제가 있다기보다는 언어로써의 위치에 문제가 있는 것 같습니다. 물론 언어 자체가 좀 재미가 없다는 사소한(?) 문제도 있긴 하죠. :-)

    그리고 C++의 template meta programming이 강력하긴 하지만 실제 현장의 프로그래머들이 작성하는 코드에서 얼마나 쓰이고 있는지는 의문입니다. 게다가 기능이 강력한 만큼 readability와 understandability가 떨어져 maintainability가 현저히 낮아지는 문제도 있습니다. 제가 속한 프로젝트에서 template이 쓰인 코드들은 전부 제가 만든 것들인데 이 코드를 본 사람들이 질문이라도 하면 어디서부터 설명해야 할지 막막할 때가 자주 있습니다. template -> template specialization까지만 이해시키려 해도 쉽지 않거든요. 암튼 template meta programming이 누구나 사용할 수 있는 유용한 개발 도구가 될지 아니면 라이브러리 제작자들이나 사용하는 도구가 될지는 좀 더 지켜봐야 할 것 같습니다.

    그리고 bloglines에 한동안 글이 없다 했는데 블로그가 접속이 안되네요. :-| 바쁘신가봐요. 얼른 다시 문 여시길 기다리고 있겠습니다. ;-)

    ReplyDelete

Post a Comment

Popular posts from this blog

1의 개수 세기 - 해답

벌써 어제 말한 내일이 되었는데 답을 주신 분이 아무도 없어서 좀 뻘쭘하네요. :-P 그리고 어제 문제에 O(1)이라고 적었는데 엄밀히 얘기하자면 O(log 10 n)이라고 적었어야 했네요. 죄송합니다. ... 문제를 잠시 생각해보면 1~n까지의 수들 중 1의 개수를 얻기 위해서는 해당 숫자 n의 각 자리의 1의 개수가 모두 몇개나 될지를 구해서 더하면 된다는 사실을 알 수 있습니다. 예를 들어 13이라는 수를 생각해 보면 1~13까지의 수에서 1의 자리에는 1이 모두 몇개나 되는지와 10의 자리에는 모두 몇개나 되는지를 구해 이 값을 더하면 됩니다. 먼저 1의 자리를 생각해 보면 1, 11의 두 개가 있으며 10의 자리의 경우, 10, 11, 12, 13의 네 개가 있습니다. 따라서 2+4=6이라는 값을 구할 수 있습니다. 이번엔 234라는 수에서 10의 자리를 예로 들어 살펴 보겠습니다. 1~234라는 수들 중 10의 자리에 1이 들어가는 수는 10, 11, ..., 19, 110, 111, ... 119, 210, 211, ..., 219들로 모두 30개가 있음을 알 수 있습니다. 이 규칙들을 보면 해당 자리수의 1의 개수를 구하는 공식을 만들 수 있습니다. 234의 10의 자리에 해당하는 1의 개수는 ((234/100)+1)*10이 됩니다. 여기서 +1은 해당 자리수의 수가 0이 아닌 경우에만 더해집니다. 예를 들어 204라면 ((204/100)+0)*10으로 30개가 아닌 20개가 됩니다. 이런 방식으로 234의 각 자리수의 1의 개수를 구하면 1의 자리에 해당하는 1의 개수는 ((234/10)+1)*1=24개가 되고 100의 자리에 해당하는 개수는 ((234/1000)+1)*100=100이 됩니다. 이들 세 수를 모두 합하면 24+30+100=154개가 됩니다. 한가지 추가로 생각해야 할 점은 제일 큰 자리의 수가 1인 경우 위의 공식이 아닌 다른 공식이 필요하다는 점입니다. 예를 들어 123에서 100의 자리에 해당하는 1의 개수는 ((123/1

std::map에 insert하기

얼마전 회사 동료가 refactoring한 코드를 열심히 revert하고 있어서 물어보니 다음과 같은 문제였습니다. 원래 코드와 refactoring한 코드는 다음과 같더군요. nvp[name] = value; // original code nvp.insert(make_pair(name, value)); // refactored 아시겠지만 위의 두 라인은 전혀 다른 기능을 하죠. C++03에 보면 각각 다음과 같이 설명되어 있습니다. 23.1.2/7 Associative containers a_uniq.insert(t): pair<iterator, bool> inserts t if and only if there is no element in the container with key equivalent to the key of t. The bool component of the returned pair indicates whether the insertion takes place and the iterator component of the pair points to the element with key equivalent to the key of t. 23.3.1.2/1 map element access [lib.map.access] T& operator[](const key_type& x); Returns: (*((insert(make_pair(x, T()))).first)).second. 원래 코드는 매번 새 값으로 이전 값을 overwrite했지만 새 코드는 이전에 키가 존재하면 새값으로 overwrite하지 않습니다. 따라서 원래 기능이 제대로 동작하지 않게 된것이죠. 그래서 물어봤죠. "왜 이렇게 했어?" "insert가 성능이 더 좋다 그래서 했지." :-? 사실 Fowler 아저씨는 Refactoring 책에서 refactoring은 성능을 optimizing하기 위한 것이 아니다라

C++ of the Day #9 - Boost.Python 사용하기 #1

Python 은 가장 인기있는 interpret 언어중의 하나입니다. Python의 장점 중 하나는 C/C++ 모듈과 쉽게 연동할 수 있다는 점입니다. 물론 손으로 일일히 wrapper를 만드는 것은 손이 많이 가고 에러를 만들수 있는 작업이나 SWIG 등과 같은 도구를 사용하면 쉽게 python 모듈을 만들 수 있습니다. Boost.Python 은 이런 SWIG와 같이 python 모듈을 쉽게 만들 수 있도록 도와주는 라이브러리로 순수 C++만을 사용한다는 점이 SWIG와 다른 점입니다. 그리고 개인적으로는 Boost 라이브러리에 포함되어 있는 것들이 왠지 좀 더 믿음직스러워서... :-) 이번 글에서는 Boost.Python 문서에 나와 있는 예제 를 가지고 간단하게 python 모듈을 만드는 방법에 대해서 알아보겠습니다. Requirements 리눅스 이 글에서는 리눅스 환경에서의 사용 방법을 설명한다. Boost.Python 라이브러리 (1.33.1) Boost 라이브러리를 다운로드받아 아래와 유사한 명령으로 라이브러리를 빌드한다. bjam -sTOOLS=gcc -with-python install bjam의 --prefix 옵션으로 라이브러리가 설치될 위치를 변경할 수 있다. Python 라이브러리 (2.4.3) Python을 다운로드 받아 빌드하여 설치한다. 위의 경우와 유사하게 configure의 --prefix 옵션으로 설치될 위치를 변경할 수 있다. Write C++ Code 다음과 같이 코드를 작성한다. // greet.cpp #include <stdexcept> char const* greet(unsigned x) { static char const* const msgs[] = { "hello", "Boost.Python", "world!" }; if (x > 2) throw std::range_error("