« Previous : 1 : ... 158 : 159 : 160 : 161 : 162 : 163 : 164 : 165 : 166 : ... 221 : Next »

1.

수학에서 원주율 pi와 자연상수 e는 가장 유명하고 친근한 상수임이 틀림없다. 이들은 유리수를 계수로 가지는 대수방정식의 근이 되지 않는 초월수임이 증명되어 있고, 이들의 정의나 다른 특성에 따라 값을 구하는 식이 여럿 존재한다.

pi = 4(1/1 - 1/3 + 1/5 - 1/7 + 1/9 ... )
e = lim (1 + 1/n )^n (n → 무한대)

pi의 저 공식은 정말 이보다 더 쉬울 수 있을까 싶을 정도로 너무 깔끔하고, 저게 도대체 파이와 어떻게 관계가 있을까 하는 생각이 들기도 한다. 사실, 저것은 arctan(tan의 역함수) 함수의 테일러 급수에다가 1을 대입하여 얻은 식이다. 45도일 때 기울기(=탄젠트)가 딱 1이 되니, 저 값은 pi/4가 나오고, 따라서 pi를 구하기 위해 4를 곱한 것이다.

e의 경우, 숫자를 무한대로 띄우는 n승과, 그 지수 연산의 발산 효과를 무효로 만드는 1/n (1에다가는 아무리 큰 지수 연산을 해도 그대로 1..)의 극한이 저렇게 가까워지는 게 신기하기 그지없다.

그러나 위의 두 공식은 둘 모두 n 값이나 항의 개수가 100000이 넘어가도 소숫점 겨우 네댓 자리까지밖에 일치하지 않을 정도로 수렴이 대단히 느린 게 흠이다. 이 방식으로는 소숫점 n째 자리까지 일치하려면 계산량이 n의 지수함수 형태로 증가해야 한다. 알고리즘으로 치면 엄청나게 비효율적인 알고리즘이다.
그래서 실제로 pi나 e의 값을 계산할 때는, 매 회에 좀 더 복잡한 계산이 수행되더라도 적은 횟수로도 더 빠르게 수렴하는 다른 식을 찾아 쓴다.

자연상수 e는 exp(x), 즉, 미분해도 자신과 동일한 함수의 테일러 급수로부터 얻은 식을 이용해 값을 구할 수 있다. 바로 n팩토리얼의 역수의 무한합인데, 직관적일 뿐만 아니라 수렴 속도도 아주 빠르다. 물론 팩토리얼이 숫자를 폭발적으로 키우는 연산이긴 하지만, 그만큼 정확도도 커져서 14!까지만 계산해도 소숫점 8~9자리까지 정확한 값이 나온다. 그래서 이걸로 끝이고 다른 계산법을 찾을 필요가 사실상 없다.

자연상수라는 이름엔  '자연'이라는 단어가 괜히 붙은 게 아니다. 얘는 수학의 입장에서는 특성이 매우 자연스럽고 직관적이고 사랑스럽기까지(!) 한 수이기 때문에, 초월수라는 증명도 상당히 초창기에 이뤄졌다. 리우빌 상수--10진법 기준 n!에 속하는 소숫점 자리만 1이고 나머지는 0인 좀 괴랄한 0.110001000… --처럼 초월수의 존재를 증명하기 위해 일부러 설정된 수가 아닌 수 중에서는 초월수라는 게 증명된 최초의 수가 바로 자연상수이다.

pi는 e보다 더 오묘한 수여서 초월수 증명도 10년 남짓 더 늦게 나왔으며, 더욱 복잡하고 다양한 유도식들이 존재한다. 특히 컴퓨터가 발명되고 원주율 계산이 컴퓨터의 성능을 측정하는 잣대로 통용되기 시작한 뒤부터는 컴퓨터의 입장에서 더욱 계산하기 쉬운 형태의 식이 연구되기 시작했다. 그래도 어느 것이든 e만치 형태가 간단하면서도 빨리 수렴하는 식은 존재하지 않는 것 같다.

비록 컴퓨터 시대에 발견된 식은 아니지만,
sqrt(2) / 2 라는 분수에서 시작하여
sqrt( 2+ sqrt(2) ) / 2와 같은 식으로 분자에다가만 x → sqrt(2 + x)로 변환을 하면서 생성된 수들을 계속 곱하면 2/파이 (파이의 역수의  두 배)에 수렴하는데,
이것도 e만치는 아니지만 계속되는 곱셈과 제곱근 버프 덕분인지 수렴 속도가 빠른 편이다.

2.

1부터 n까지 자연수가 있고 이들의 역수를 나열하면 조화수열이 된다. 조화수열의 무한합은 비록 무지막지하게 느리지만 무한대로 발산한다는 것이 알려져 있다.
한편 n!의 역수의 무한합은 아까 말했듯이 e가 되는데,
그럼 n^2의 역수의 무한합은 무엇이 될까?

거듭제곱의 지수가 1보다 커지는 순간부터 역수의 무한합은 유한한 값으로 수렴하긴 한다. 그러나, 그 수렴값의 특성을 알아내는 건 아주 어려운 일이어서 지금까지도 알려진 게 그리 많지 않다.
n^x의 역수의 무한합을 일반적으로 x에 대한 리만 제타 함수값이라고 한다.

그리고 그 x가 짝수일 때는 놀랍게도 pi와 관련이 있는 값으로 수렴한다는 것이 천재 수학자 오일러에 의해 밝혀졌다. 가령, 2일 때는 이렇다.

1 + 1/4 + 1/9 + 1/16 + 1/25 ... = pi^2 / 6

그러니 이것도 응당 파이를 구하는 데 쓸 수 있는 공식이다.
비록 이것 역시 아까의 1/3 1/5 1/7 공식 만만찮게 수렴 속도가 몹시 느리기 때문에 실용성은 떨어지지만 말이다.

여기서 갑자기 pi가 튀어나오는 것도 신기하지만, 이 수의 진짜 놀라운 면모는 또 다른 곳에 있다.
바로 이 수는 자연수에 존재하는 소수의 분포와 관계가 있다.

1부터 N 사이에 있는 임의의 두 자연수를 뽑았을 때 이것이 서로 소일 확률은 N이 커질수록 바로 저 수, 다시 말해 (pi^2)/6의 역수로 수렴한다! 그 확률은 대략 60.8%이다.

아니, 리만 제타 함수에서 파이가 왜 조건부로 튀어나오며, 게다가 미적분· 해석학하고는 아무 관계가 없는 정수론과 관련된 의미가 왜 이런 데서 발견되는 걸까? 이걸 알아 낸 사람도 오일러이다.

2를 포함해서 리만 제타 함수의 짝수승의 값은 그래도 pi가 얽혀 있으니 초월수라는 건 확실히 인증이다만 홀수승의 값들은 무리수인 것만 알려져 있지 다른 특성은 알려진 바가 없다. 초월수일 게 거의 확실시되고 있긴 하나, 그조차도 정식으로 증명되지는 못했다.

3일 때의 값은 '아페리 상수'라 하여 일부 공학 분야에서도 쓰인다. 이 수가 무리수라는 증명을 1978년에 한 프랑스의 수학자 아페리의 이름을 딴 것이다. 정의대로 각 자연수들을 3승한 뒤 역수를 구해서 더하면 값을 구할 수 있지만, FM 방식은 역시 수렴이 더디기 때문에 훨씬 더 복잡하지만 더 빨리 수렴하는 별도의 급수 전개를 써서 값을 구한다.

그런데 이 값의 역수는 임의의 세 자연수가 서로 소일 확률이라고 한다. 마치 가위바위보에 참여하는 인원이 많아질수록 무승부가 나올 확률이 치솟듯, 세 자연수는 두 자연수일 때보다 서로 소일 확률이 올라가서 그 값은 약 83.1%에 달한다.

작은 범위에서라도 정말 얼추 그렇게 되는지는 프로그램을 짜서 간단히 확인해 볼 수 있다.
1부터 N까지 3중 for 문을 작성해서 모든 숫자 조합에 대해서 최대공약수를 구해서 1이 나오는 경우를 세면 되는데, N이 몇천 정도만 돼도 3중 for 문은 오늘날의 최신 컴퓨터에서도 버벅대는 작업량이다.

3.

그래서 본인, 이 기회에 멀티코어 프로그래밍을 실습해 봤다.
i5 쿼드코어답게 CreateThread 함수로 스레드를 4개 만들어서 뺑뺑이를 돌리는 분량을 인위로 분할한 뒤, 계산 결과를 취합했다. 그랬더니...

809615629/973620600 0.831551 (7753)
809615629/973620600 0.831551 (3448)

코어 하나밖에 못 쓰고 고로 CPU를 최대 25%만 쓰던 싱글스레드 오리지널 코드는 7.8초가 걸린 반면,
스레드를 여러 개 만드는 코드는 CPU를 95% 가까이 점유하면서 제 속도를 내더니, 싱글스레드의 절반이 채 안 되는 3.5초 남짓한 시간 만에 처리를 다 끝내는 걸 알 수 있었다. 실제로 서로 소인 조합이 전체의 83.1%가량을 차지하는 것도 사실이었다.

요즘 세상에 CPU를 70% 이상 한꺼번에 점유하는 프로그램을 내 손으로 직접 만들 일은 없었는데 참 오랜만에 보는 광경이었다. 요즘은 단일 프로그램이 CPU 성능을 제대로 활용하려면 이런 식의 테크닉을 동원해야 한다는 걸 더욱 절실히 느끼게 됐다.

Posted by 사무엘

2012/06/07 08:21 2012/06/07 08:21
, ,
Response
No Trackback , 9 Comments
RSS :
http://moogi.new21.org/tc/rss/response/692

우주로 나간 개, 라이카

2003년에 콩코드 여객기가 퇴역한데 이어 2011년엔 미국의 우주 왕복선들도 전량 퇴역했다. 이제는 천조국 미국도 우주 정거장으로 사람을 보내려면 러시아의 소유즈 우주선을 택시 삼아 얻어 타야 한다. 그래도 우주 왕복선이 소유즈보다 더 커서 사람이나 짐을 더 많이 실을 수 있었는데, 아쉬운 점이다.

오로지 학술 목적으로만 우주선을 띄우던 미국 NASA와는 달리, 돈맛을 알아 버린 러시아는 세계의 민간인 억만장자들을 상대로 우주 관광 사업을 벌이고 있다. 잘 알다시피 우리나라의 이 소연 씨를 포함해 전세계적으로 잘 알려진 우주 관광객들도 다 미국이 아니라 러시아를 통해서 우주에 갔다 온 사람이다.
누구든 한 200억 원 정도만 주면 갈 수 있다. 참 쉽죠?

요즘은 그래서 다시 러시아의 우주 기술이 주목을 받고 있는 추세인데, 그 러시아, 아니 구소련은 냉전 시절에 얼마나 공돌이들을 갈아 넣으면서 빡세게 기술을 개발했을지 상상이 되겠는지? 지금으로부터 50여 년 전으로 되돌아가 보겠다.

전산학계에 앨런 튜링이나 폰 노이만 같은 괴수가 있었다면, 항공 우주 공학계에는 로버트 고더드(액체 연료 로켓의 근간을..)부터 시작해 미국에는 폰 브라운, 소련에는 세르게이 코룔로프 같은 진짜 우주덕, 우주 괴수들이 있었다. 일반 항공기처럼 하늘에 잠깐만 떴다가 다시 내려오는 게 아니라 아예 지구 중력을 벗어나서 궤도에 진입하려면 가히 넘사벽급의 동력이 필요하고 이를 제어하는 최첨단 기술이 필요하다. 거기에다 '재돌입 성공' + '사람까지 태우고' 같은 단서가 추가되면 흠..;;

그런 우주 시대의 서막을 연 것은 1957년 10월, 세계 최초의 인공위성인 스푸트니크 1호이다. 이에 고무된 소련은 우주에 생명체를 보내는 게 가능하겠는지를 실험하게 되었다.

그래서 아직 1호가 지구를 돌고 있을 때에 소련은 1호보다 훨씬 더 크고 무거워진 스푸트니크 2호를 뒤이어 발사했는데, 이때는 개를 한 마리 실었다. 그 이름도 유명한 '라이카'라는 암캐이다.
약을 하나 새로 개발해도 임상실험은 먼저 동물에게 하지 않던가. 라이카는 지구 궤도로 나간 최초의 포유류가 되었다.

원래 라이카는 모스크바 길거리에서 주인을 잃고 배회하는 '똥개'였다고 한다. 그런데 어쩌다 보니 소련의 과학자들의 눈에 띄여서 나름 우주의 상황을 상상한 여러 훈련을 시켰다. 물론 얘만 수집한 건 아니지만 여러 후보들 중에서 라이카가 훈련 성적이 가장 좋았다. 사람 말을 잘 듣고 험악한 환경에서도 돌발행동 안 하고, 좁고 어두운 곳에서 얌전히 잘 견디는 성격이었던지라 최종 실험 대상으로 선택되었다.

전해지는 일화에 따르면, 라이카를 맡아서 훈련시키는 일을 한 모 과학자는, 그 개를 발사체에 태워서 공중에 잠시 띄웠다가 다시 돌아오게 하는 줄로만 알았다고 한다. 그러나 나중에 알려진 사실은 예상과 달랐다. 그 당시 소련은 우주 발사체를 무사히 재돌입시켜서 귀환시키는 기술이 아직 없었기 때문에, 스푸트니크 2호는 1호와 마찬가지로 지구를 빙빙 돌다가 대기권으로 떨어져서 마찰열로 인해 유성처럼 불타 없어질 운명이었던 것이다.

그래서 우주선에 탄 개에게는 발사 1주일 후에 독극물이 든 마지막 식사를 제공하여, 인공위성이 아직 지구 궤도에 있을 때 먼저 안락사시킬 계획이었다.
이 사실을 안 과학자는 통곡했지만, 국가의 프로젝트 계획이 그러한데 딱히 어쩔 도리가 없었다고 한다.

스푸트니크 2호는 1957년 11월, 1호가 발사된 지 거의 정확히 한 달 뒤에 발사되었다. 라이카는 온몸이 묶여 감금되다시피하고, 생체 상태를 실시간으로 측정하여 전달하는 장비가 부착되었다. 이거 무슨 카미카제 특공대에 출격하는 전투기 조종사 같다..;;

사용자 삽입 이미지
인공위성 자체는 성공적으로 발사되어 지구 궤도에 잘 진입했다. 1호도 모자라서 1개월 간격으로 2호까지 성공하자 미국은 가히 스푸트니크 쇼크라고 불리는 충격과 공포를 경험하면서 소련의 발달된 우주 기술에 전율하게 되었다.
이에 기세등등해진 소련은, 스푸트니크 2호가 성공적으로 잘 발사되었으며 안에 태운 라이카는 잘 살아 있다가 (유감스럽지만 그래도) 계획대로 1주일째에 안락사를 당했다고 공식 발표했다.

그러나 그로부터 수십 년이 지나서야 폭로된 비밀 문서에 따르면, 라이카는 며칠 못 가 더 일찍 죽었다는 것이 드러났으며, 궁극적으로는 발사된 지 겨우 5~7시간을 못 넘기고 고온· 고압과 굉음으로 인한 극심한 스트레스에 시달리면서 꼼짝도 못 한 채 고통스럽게 죽었음이 드러났다.

고온이라는 게 화상을 입을 정도의 뜨거운 온도이다. 수 시간 후부터 라이카의 심장 박동은 이미 감지되지 않았으니, 그때 진실을 알고 있었던 과학자들의 상심이 컸겠다. 그 원인으로는 선실에다 산소를 공급하고 온도를 조절하는 장치가 제대로 동작하지 못한 것으로 추정되었다.

스트레스가 극심했을 때는 라이카의 심장 박동수는 평소의 3배가 가깝게 뛰었다고 한다. 죽기도 그렇게 죽었을 뿐만 아니라 시체도 재돌입 과정에서 인공위성과 함께 공중분해되었을 터이니 확실한 끔살 인증이다.

라이카는 비록 살아서 돌아오지는 못했으나, 이 실험을 통해 지구 생명체가 지구 궤도에 진입하는 과정과 무중력 상태에 견딜 수 있음이 확인되었으며, 과학자들에게 우주공간에서 생명체 반응에 대한 귀중한 데이터를 제공하였다. (출처: 위키백과) 라이카의 죽음은 '개죽음'으로 그치지 않았다.
짧게나마 그래도 궤도에 진입하여 무중력 상태가 될 때까지 살아 있었기 때문이다. 정말로 사람이 아니라 동물부터 먼저 보내 보길 잘한 것이다.

라이카에 대한 비화가 알려지면서 라이카는 일약 영웅이 되었다. 좀 감수성 예민한 사람이 들으면 눈물이 핑 돌 정도의 스토리이지 않은지? 노래나 만화 같은 매체에도 등장하였으며 외국의 어느 동물 보호 단체에서는 이런 식으로 우주 개발을 강행한 소련 정부를 비판하는 한편으로 라이카의 기일에 맞춰 묵념까지 했다고 한다.

인류의 우주 개발을 위해 희생된 개 한 마리에게도 사람들이 이렇게 연민과 동정을 아끼지 않는다.
그 반면, 인류의 죄를 대속하기 위해서 희생된 어린양을 믿는 게 성경의 기독교라고 이해하면 되겠다.

다음은 추가 설명들.

1. 인간을 우주에 보내기 위해서 진공 상태가 생체에 어떤 영향을 끼치는지, 그리고 무중력 상태에서 얼마나 잘 버틸 수 있는지를 아는 것은 필수였다. 이 분야는 인류 역사상 정말로 경험 데이터가 전무했기 때문이다.
하긴, 과거에 일본군 731 부대가 행한 생체 실험 중에도 진공 실험이 있긴 했다. ㄷㄷㄷ

2. 소련의 도발에 다급해진 미국은 자기네도 황급히 인공위성을 쏘아 올리려 뱅가드(Vanguard) 로켓을 몇 차례 발사하였으나 발사 2초 만에 폭발해 버리는 등, 보기 좋게 실패하고 말았다. 그 뒤에 공돌이들을 더 갈아 넣으면서 연구한 끝에 1년 남짓 뒤인 주노 로켓으로 1958년에야 미국 최초의 인공위성인 익스플로러 1호를 띄운다..

3. 소련에서는 그 뒤 1960년 8월 스푸트니크 5호에 스트렐카와 벨카라라는 2마리의 우주견을 태웠는데, 이들은 지구궤도를 17바퀴 돈 다음 귀환함으로써 지구 궤도 비행 후 살아 돌아온 최초의 생명체가 되었다.

4. 라이카처럼 저렇게 사람을 살리고 자신은 죽은 유명한 개로 한국에는 군견 헌트가 있다. 1990년 3월, 현재로서는 마지막으로 발견된 북한의 남침용 땅굴인 제4 땅굴이 발견되었다. 당시, 헌트는 갱도 내에서 화약 냄새를 맡고 뛰어가다가 땅굴 내부의 목함 지뢰를 밟고 산화하였다. 소대원들의 목숨을 구했음은 두말할 나위도 없다.

군대에서 선임병이 후임을 놀릴 때 “군견이 너(졸병)보다 계급이 높기 때문에, 보면 경례해라”라고 하는 경우가 있는데 그 말은 물론 농담이다. 하지만 헌트는 사후에 진짜로 소위 계급이 추서되었으며, 땅굴 입구에 무덤과 동상, 추모비까지 건립되었다.

5. 라이카는 참고로 사막의 인간 식인범인 라이’타’하고는 관계 없다. 혼동하지 말 것. ㅠㅠ

사용자 삽입 이미지

Posted by 사무엘

2012/06/04 08:24 2012/06/04 08:24
,
Response
No Trackback , 2 Comments
RSS :
http://moogi.new21.org/tc/rss/response/690

한강을 잇는 다리들

※ 다리 한번 많기도 해라. 설명은 서쪽(하류)에서 동쪽(상류) 순이다. 당신은 두 개의 다리 이름 A, B가 주어졌을 때, 이들의 위치 관계를 비교할 수 있겠는가? (정렬용 비교 함수 ㄲㄲ)

방화 대교: 인천 공항 고속도로(130)와 통하는 다리이다. 6차선.

마곡 철교: 공항 철도가 다니는 교량이다. DMC - 김포공항 역 사이에 있다.

가양 대교: 21세기에 건설되었고, 교각들 사이의 간격이 굉장히 긴 다리라고 한다. 6차선.

성산 대교: 국도 1호선의 일부이다. 남쪽으로는 서부 간선 도로로 이어지고, 북쪽으로는 내부 순환로와 연결된다. 6차선.

양화 대교: 국도 6호선의 일부인 8차선 교량으로, 중간에 선유도를 경유한다.

당산 철교: 양화 대교 바로 근처에 있으며, 서울 지하철 2호선이 다니는 교량이다. 기존 교량이 1990년대 중후반에 철거되고 재건설된 이력이 있다.

서강 대교: 여의도로 가는 3대 교량 중 하나로, 6차선이다. 여의도의 서쪽을 경유하다 보니 국회 의사당과 가장 가깝다. 교량 중간에는 밤섬을 경유한다. 북쪽으로는 광흥창과 신촌 역을 찍고 연세대 방면으로 간다.

마포 대교: 자동차 교량들 중에는 꽤 오래 전에 건설되었고 현재는 차선수도 10차선으로 폭이 매우 크다. 여의도의 정중앙을 관통한 뒤부터 경인선 철도를 따라 간다. 국도 46호선의 일부 구간.

(서울 지하철 5호선이 지나는 하저 터널은 북단은 마포 대교 쪽에 있지만 남단은 한 블록 내려간다. 그래서 여의도와 여의나루 역은 국도 46호선상에 있지 않다.)

원효 대교: 마포 대교의 혼잡을 완화하기 위해 보조로 건설된 다리이다. 4차선으로 좁은 편.

한강 철교: 한강에 건설된 최초의 다리. 오늘날은 단선 교량 2, 복선 교량 2로 선로가 6개나 있는 크고 아름다운 3복선 교량의 세트가 되어 있다.

한강 대교: 한강 철교의 도로 equivalent로, 역시 그 전신은 한강 철교와 마찬가지로 일제 강점기에 이미 건설되었으니 역사가 깊다. 중간에 노들섬을 경유한다. 6· 25 때 폭파되었던 다리가 바로 이 지점에 있던 다리이다. 현재는 8차선이다.

동작 대교: 서울 지하철 4호선과 자동차가 공용하는 6차선짜리 다리이다. 남쪽으로는 동작 대로로 이어지지만 미군 기지 때문에 북쪽으로는 더 곧게 이어지는 길이 없다.

반포 대교: 우리나라 최초의 복층 교량으로, 아래에 잠수교가 있다. 폭은 6차선. 남쪽으로는 반포 대로를 타고 고속버스 터미널과 서초 역, 예술의 전당까지 쫙 내려가며, 북쪽으로는 남산 3호 터널로 갈 수 있다.

한남 대교: 아래로는 경부 고속도로 및 강남 대로와 직결하고, 위로는 남산 1호 터널과 직결하는 매우 중요한 다리이다. 덕분에 무려 12차선이나 되어, 2위인 마포 대교까지 제치고 서울에서 차선수가 가장 많은 다리이다.

동호 대교: 서울 지하철 3호선과 자동차가 공용하는 다리이다. 4차선이고 남북으로는 인근의 다리만치 지리적으로 중요한 경로가 없으며, 게다가 강변 북로에서는 이 다리로 진입하거나 이곳으로 나갈 수 없기 때문에 존재감이 다소 덜하다.

성수 대교: 한때 붕괴 사고로 인해 악명을 떨친 다리. 현재는 8차선이다.

(실드 공법으로 건설되었다는 분당선 하저 터널이 이 사이를 지날 예정.)

영동 대교: 국도 47호선의 일부이다. 박통 시절에 한창 개발 중이던 강남 지역의 발전을 촉진한 교량이라 함. 6차선 크기이다.

청담 대교: 자동차와 서울 지하철 7호선이 공용하는 6차선 복층 교량이다. 그 특징에 대해서 예전 글에서 잘 소개한 적이 있으므로 더 자세한 설명은 생략함. 영동 대교와 꽤 가까이 있다.

잠실 대교: 국도 3호선의 일부인 8차선 교량. 이 다리를 타고 남쪽으로 가면 잠실 역과 송파 대로가 나온다. 마포, 한남과 더불어 가장 오래 된 다리 축에 든다.

잠실 철교: 서울 지하철 2호선이 다니는 교량이다. 하지만 양 옆으로 자그맣게 자동차 도로도 생겨서 이름의 의미가 약간 므흣해졌다.

올림픽 대교: 동서울 터미널을 드나드는 버스가 즐겨 건너는 6차선짜리 다리이다. 사장교 형태이고 다리 중앙에 높이 88m짜리 주탑이 있는 걸로 유명하다. (참고로 대전 한빛탑의 높이는 93m!)

천호 대교: 국도 43호선의 일부인 6차선 교량이다. 이 다리는 한강의 선형상 지도 위상으로 볼 때 수직(남북)이 아니라 수평(동서)에 가깝다. 천호 대로로부터 이어지고 동쪽으로 계속 가면 하남시가 나온다. 서울 지하철 5호선이 지나는 하저 터널이 정확히 아래에 있다. (광나루-천호. 마포-여의나루 구간은 마포 대교와 정확히 평행이 아니다.)
인근에는 광진교라는 미니 다리도 있다.

강동 대교: 서울 외곽 순환 고속도로가 지나는 다리.
미사 대교: 서울-춘천 고속도로가 지나는 다리.

Posted by 사무엘

2012/06/02 08:24 2012/06/02 08:24
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/691

컴퓨터의 모니터 화면을 픽셀 단위로 있는 그대로 저장하는 기능의 필요성은 과거 도스 시절부터 쭉 있어 왔다. 프로그램의 기능을 설명할 때, 특정 인증샷을 남길 때 등 여러 모로 유용하고 필요한 기능이기 때문이다.

오늘날에도 키보드에는 print screen이라는 키가 있다. 옛날에는 사용자가 이걸 누르면 하드웨어 차원에서 인터럽트가 발생하여, 텍스트 모드 화면에 찍혀 있던 글자가 프린터 포트(LPT1)로 진짜로 갔다. 프린터가 안 켜진 상태에서 이걸 누르면 컴퓨터가 멎었다. pause를 누르면 컴퓨터의 전체 작동이 일시 중단되고 ctrl+alt+del을 누르면 컴퓨터가 곧바로 재부팅되던 시절의 얘기이다.

하지만 이런 기본 기능은 너무 원시적이고 빈약하며, 그래픽 모드에 대한 대비책이 전무했기 때문에 화면 캡처는 결국 소프트웨어 계층이 담당하는 영역으로 넘어가게 되었다.

도스 시절의 화면 캡처 프로그램은 응당 램 상주(TSR) 프로그램의 형태였다. 아래아한글의 경우, 1.5x에서는 hcopy.exe라는 자그마한 유틸리티가 있었는데, 텍스트 모드 아니면 무려 허큘리스 단색 그래픽 모드만 지원했었지 싶다. 2.0과 그 이후부터는 별도의 유틸리티는 없어졌고 대신 아래아한글 프로그램에 자체적으로 화면 캡처 기능이 들어갔다. 언제라도 Alt+키패드 +키를 누르면 지금 화면을 PCX 그림 파일로 저장할 수 있었다.

한동안 본인이 사용했던 프로그램은 수채화라는 그래픽 프로그램에 내장되어 있던 snap이라는 덜 유명한 국산 프로그램, 그리고 Screen Thief라는 비교적 유명한 외국산 프로그램이다 ST는 아주 특이하게도 텍스트 모드도 색깔과 바이오스 글꼴이 모두 가미된 그래픽 원형 그대로 캡처해 주는 끝내주는 기능이 있었다. 생성되는 그림 파일 확장자도 GIF로, 비록 오늘날의 JPG에 비할 바는 아니지만 PCX보다야 더 무겁고 압축률이 뛰어난 포맷이었다.

도스에서 윈도우로 넘어가면서 화면 캡처는 굉장히 쉬워졌다. 여러 프로그램들을 동시에 띄우고 드나들 수 있는 멀티태스킹 환경일 뿐만 아니라, 언제든지 Print Screen만 누르면 화면 전체 또는 활성화된 창이 비트맵 형태로 클립보드에 저장되기 때문이다. 그래픽 하드웨어가 워낙 겹겹이 잘 추상화되다 보니, 화면 캡처란 이제 기술적으로 대상 윈도우의 DC 내용을 내 DC로 Bitblt하는 것이 전부이다.

너무 간단해졌다. 옛날에는 하드웨어 가속을 받는 동영상이나 일부 게임 화면은 이 방법으로 캡처할 수 없어서 별도의 특수한 프로그램을 써야만 했지만 이것도 비스타부터는 옛말이 됐다.

기본적인 기능은 운영체제가 자동으로 제공해 주니, 캡처 유틸리티들은 편의성을 더욱 강화하고, 일정 수준 이상의 그래픽 편집과 보정 기능도 갖추는 방향으로 발전하기 시작했다. 여러 윈도우의 동시 캡처, 자동 스크롤 캡처, 그리고 현재 화면보다 더 큰 해상도를 가장한 화면 캡처, 멀티모니터 지원, 텍스트 정보 추출 등, 화면 캡처라는 주제 하나만으로도 기발한 아이디어가 적지 않다. 편집 쪽도 Blur 같은 전문적인 사진 보정보다는, 색깔 추출, 디더링 같은 산술적인 변환 기능이 더 필요할 것이다.

본인은 옛날에는 동영상 화면의 캡처를 위해 HyperSnap이라는 프로그램을 잠깐 썼었는데 나름 굉장히 잘 만든 프로그램이었다. 하지만 지금은 그거 없이도 동영상 화면 캡처가 얼마든지 가능해졌기 때문에 안 쓴다. 그냥 옛날 Paint Shop Pro가 같이 제공하는 화면 캡처 기능을 유용히 쓰는 중이다.
오늘날은 국산 프로그램으로는 ‘오픈캡처’가 이 분야에서 아주 유명하다. 완전 무료 프로그램이다가 최근에는 기업 대상으로 유료화되었다.

윈도우 환경이라도 게임들 역시 전통적으로 화면 캡처 기능을 제공해 왔다. 옛날에 256색 게임들은 운영체제의 print screen 키로는 비록 비트맵 데이터는 화면 캡처가 되지만 팔레트 정보가 저장되지 않아 화면이 이상한 색으로 저장되곤 했기 때문이다. 한편, 요즘은 컴퓨터의 성능이 놀랍도록 좋아지고, UCC 만들기가 보편화하면서 아예 게임 화면 동영상을 찍는 기능도 쓰이고 있다. 외산으로는 프랩스(Fraps), 국산으로는 반디캠 같은 프로그램이 좋은 예이다.

화면도 나오고 동영상도 나왔으니, 글을 맺기 전에 소리 캡처에 대해서도 잠깐만 언급하겠다. XP 이하 시절에는 내 컴퓨터에서 나오는 소리를 도로 녹음하는 것이 비교적 쉽게 가능했는데 비스타부터는 그게 방법이 꽤 까다로워지고, 하드웨어 환경에 따라서는 아예 불가능한 컴퓨터까지 생겼다고 본인은 알고 있다. 비스타 때부터 비디오와 오디오의 하드웨어 계층이 바뀌었다.

개인적으로, 키매크로 유틸리티와 저런 부류의 캡처 유틸리티는 운영체제가 기본 제공할 만한 보조 프로그램의 아주 좋은 예라고 생각한다. 컴퓨터의 활용 능력 및 생산성하고 직접적인 관계가 있는 도구이기 때문이다.

Posted by 사무엘

2012/05/31 08:36 2012/05/31 08:36
Response
No Trackback , 2 Comments
RSS :
http://moogi.new21.org/tc/rss/response/689

1.

서울대 김 빛내리 교수라고 우리나라에서 미생물학 내지 bio-informatics 분야의 최고의 전문가가 계신다. 최종 학력은 옥스퍼드 대학 박사. 대외적으로 쓰는 영어식 이름은 Narry Kim이라고 한다. (오옷) 연구실 이름은 “RNA 생물학 연구실”.

이분은 외국의 저명한 일류 학술지에 수시로 논문을 냈으며 한국의 촉망 받는 여성 과학자로 이미 여러 번 선정되고 상도 받고 언론도 탔다. 진짜 이름값 하는 인생을 살았다. 허나 본인은 생물학에 완전 문외한인 관계로, 이분 소개는 예전에 이 광근 교수 같은 분을 소개할 때만치 자세하고 정확하게는 못 한다는 점을 양해 바란다. ^^

본인의 지인 중엔 대학원에 진학하여 생물학 쪽으로 공부를 계속하는 친구가 있다. 얘기를 나누다가 내가 먼저 김 빛내리 교수 얘기를 꺼내자 걔도 “어, 너도 그분이 누군지 아는구나.” 하고 반가워했다. ^^

비슷한 예로, 본인의 동창 중에는 정말 학창 시절 내내 수학과 물리만 파면서 살던 덕후로, 과학고와 카이스트의 설립 취지에 가장 부합하며 사는 놈이 하나 있었다. 걔에게는 “잘은 모르겠지만 네 연구 분야가 그럼 이 휘소 박사가 파던 분야하고 비슷한 거냐?”라고 거들먹거려 주니까 걔 역시 반가워하더라.

2.

난 강 용석 씨가 한창 안 철수 씨를 때리는 글을 블로그에다 올릴 때 그의 글을 통해서 김 교수에 대해서 처음으로 듣게 됐다. 세상에 그런 엄청난 연구 업적을 남긴 사람도 그 긴 시간 강사 생활을 거쳐서 힘겹게 교수에 임용되고 그 짬밥에 아직도 조교수· 부교수 급인데, 안 철수 부부에게 주어진 서울대 정교수 특혜는 해도 해도 너무한 사기 수준이라고 말이다.

본인 역시 안 철수 씨가 머리와 노력을 겸비한 의학도 출신의 엄친아 수재이고 왕년의 컴덕후 겸 훌륭한 기업인· 경영자인 것은 응당 인정한다. 하지만 교수 세계에서 그 정도로 급격한 진급이 합당할 정도로 ‘세계적인 석학’이라는 것에는 동의하지 않는다. 안 씨가 허접해서가 아니라 학계에는 유명세만 안 탔을 뿐이지 안 씨보다 더한 우주괴수들도 많기 때문이다. 빌 게이츠나 스티브 잡스가 ‘석학’은 아니잖아?

뭐, 강 용석 씨 자신도 그야말로 어마어마한 스펙과 프로필을 자랑하는 똘똘이 수재이며 한때는 시사와 관련하여 굉장히 통렬하고 날카롭고 속 시원한 글을 많이 올리긴 했다. 하지만 좀 싸가지 없는 말투와 교만과 오만방자함 때문에 지금은 다시 버로우 탄 듯하다. 조금만 고개를 숙일 줄 아는 안목이 아쉬웠다. 지금까지 실패를 모르고 빳빳하게만 살아 와서인 걸까.

3.

김 빛내리 교수 말고 본인이나 평범한 한국인들이 아는 유명한 여성 과학기술인으로는 역시 윤 송이 씨가 있다. 학력은 잘 알다시피 서울 과학고에 카이스트를 거쳐 MIT 박사이다. 모교로 돌아와 교수의 길을 갈 법도 한 진정한 엄친딸이지만, 이분은 그냥 곧바로 기업체로 간 경우이다. 사실 윤 씨는 일단 진로 자체가 과학자보다는 공학자에 훨씬 더 가깝기도 했고 말이다. 그리고 어차피 억만장자가 됐을 터이니 굳이 교수 자리가 아쉬울 필요도 없다.

윤 송이 씨의 여동생인 윤 하얀 씨는 언니의 명성에 너무 가려져서 조명을 못 받았을 뿐이지 역시 만만찮은 천재로, 하버드 대학에 진학하여 과학자의 길을 갔다고 알려져 있다. 분야는 생물학. 뭐, 본인이 고등학교를 다니던 때만 해도 선배나 동기 중에 남매가 나란히 동일한 과학고에 입학하는 흠좀무스러운 형제· 남매가 있긴 했다.

윤 씨와 달리 김 교수가 과학고 출신이 아닌 이유는, 그 시절에 아직 주변에 과학고가 없었기 때문이라 한다. 김 교수는 1969년생. 서울 과학고가 1989년 개교이니, 그 시절엔 한국의 과학고 1호인 경기 과학고밖에 없었다. 윤 송이 박사가 1975년생이고 아마 서울 과학고의 초창기 졸업생이지 싶다.

4.

김 빛내리 교수와 상당히 비슷한 이름이 옛날에는 범죄의 안타까운 희생자의 이름으로 등장한 적이 있다. 바로 1997년의 ‘박 초롱초롱빛나리 양 유괴· 살해 사건’인데 기억하는 분 있는가? (자동사와 타동사의 차이밖에 없다.) 언론에서는 편의상 줄여서 ‘박 나리’ 양이라고 일컫기도 했다.

이 사건은 피해자가 특이하게 긴 이름을 갖고 있고, 또 가해자는 겨우 20대 후반의 면식범 임산부였기 때문에 국민들에게 끼친 충격이 굉장히 컸다. 가해자의 남편은 경찰에게 체포되어 끌려가는 아내에게 거의 멘탈 붕괴 상태로 “○○야, 설마 네가 이런 끔찍한 짓을 저질렀을 리가 없지? 제발 아니라고 얘기해 줘!”라고 절규하기도 했다.

못 말리는 된장녀 기질과 과시욕, 외국 유학을 갔지만 적응 못 하고 다시 돌아온 것, 입만 열면 횡설수설 거짓말, 자기 잘못은 인정 안 하고 끊임없는 변명 등을 보아하니, 본인은 가해자인 전 현주 씨의 모습이 21세기를 풍미한 희대의 또라이 사기꾼인 신 정아 씨하고 굉장히 닮았다는 생각을 했다. 비록 겉으로 드러나는 범죄 내역은 성격이 차이가 있지만, 둘 다 비슷한 종류의 정신병 기질이 아닌가 싶다.

법조인들이 보기에도 전 씨는 죄질이 매우 나빠 보였고, 그래서 엄벌이랍시고 무기징역이 선고되었다. 8살짜리 딸을 잃은 유가족들은 형량이 너무 가볍다고 분노했다. 그래도 가석방이나 사면 따위 없이 전 씨는 지금까지도 40이 넘은 나이로 교도소 복역 중이라고 한다. 그녀의 남편은 당연히 이혼을 했으며, 그때 그녀에게서 태어난 아기는 진작에 외국으로 입양되었다. 쩝~
(애초에 저 여자, 결혼은 어떻게 했는지가 궁금하다. 온갖 감언이설로 자기 처지를 속이고 남자를 속였지 싶다.)


김 빛내리 교수로 얘기를 시작했는데 정작 이분 자체에 대한 얘기는 별로 못 하고 같이 덩달아 떠오르는 주변 지식 얘기만 잔뜩 늘어놓게 됐다. ^^
참고로 한글 학회 직원 중에도 ‘김 한빛나리’ 선생님이 계신데, 이분은 남성임.

Posted by 사무엘

2012/05/29 08:40 2012/05/29 08:40
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/688

철도 건널목 이야기

철도와 일반 도로가 평면 교차하는 곳에는 잘 알다시피 건널목이라는 게 설치된다.

사용자 삽입 이미지

철도 차량은 어지간한 육상 자동차하고는 잽도 안 되는 어마어마한 질량을 갖고 있고 이를 바탕으로 억소리나는 운동 에너지를 자랑한다. 그렇잖아도 이렇게 무거운 데다 쇠로 된 바퀴로 쇠로 된 길을 달리기 때문에 철도 차량은 가감속이 무진장 더디다. 새마을호가 비상 제동 수준의 강한 제동을 걸어야 시속 100km 상태에서 무려 600m를 더 진행한 후에야 완전히 멈춰 설 수 있을 정도라고 알려져 있다.

이것이 무엇을 의미할까? 열차가 한번 달리기 시작하면 주변의 다른 사람이나 자동차들이 알아서 피해야 한다. 열차는 장애물 앞에서 딱 멈춰 설 수가 없다.
그러니 건널목에서는 열차의 통과 우선순위가 언제나 갑이다. 사람과 자동차들이 기다리지, 열차가 잠시 멈췄다가 사람과 자동차들을 피해 다니는 일은 없다.

건널목에서 충돌 사고가 났다 하면 육중한 열차는 하나도 탈이 없지만 자동차는 형체를 알아볼 수 없는 개발살이 난 채로 수십~100수십 미터를 질질 끌려가며, 사람이 치이기라도 하면 즉시 끔살 당한다. 철도 차량 객차 내부에 안전벨트가 없는 이유를 알 수 있을 것이다. 자동차와는 달리, 사람이 밖으로 튕겨져 나갈 정도로 급정거를 할 일이 없기 때문.

물론, 속도를 주체하질 못한다는 특성이 장점으로만 작용하는 건 아닌지라, 장애물과 충돌한 열차가 그래도 뒷부분이 도무지 멈춰 서질 못한 나머지 탈선해서 앞 객차를 타넘고 오르는 일이라도 생기면, 철도로도 대형 인명 참사가 얼마든지 생길 수 있다.

건널목에도 세 가지 모델이 있어서 비교적 통행량이 많은 건널목은 표지판+노란 색의 차단기+경보기 3콤보가 모두 갖춰져 있지만, 잉여스러운 장소에는 몇몇 요소가 생략된 건널목도 있다. 자기 폐색 구간에 열차가 바퀴가 닿은 게 감지되면 띵동~ 띵동~ 소리와 함께 차단기가 내려온다. 그리고 열차가 다 지나가면 다시 차단기는 올라간다.

우리나라의 교통 관련법에 의하면 철길과 도로의 교차는 45도보다 작은 각도로 하지는 않게 되어 있다. 대부분이 90도 수직이지만, 그래도 시골 도로를 보면 예각 교차도 그럭저럭 볼 수 있다. 건널목 사고를 줄이기 위해 요즘은 많은 건널목이 입체화되었으며, 특히 오늘날 새로 건설하는 철도는 건널목을 전혀에 가깝게 만들지 않는다고 생각하면 된다.

그럼에도 불구하고 경원선은 서빙고 역 일대와 회기-외대앞 역 사이에 일반열차도 아닌 전동차가 수시로 다니는 선로에 건널목이 있으니 안타까운 현실이다. 어서 입체화가 되어야 할 것이다.

본인은 어렸을 때 주변에 중앙선 철길이 있는 지역에서 자랐다. 철길이 지역을 심리적으로 양분하는 효과는 무척 컸다. 옛날에 나라 분위기가 더 살벌하던 시절에는 ‘철길로 다니지 맙시다’와 더불어, 레일 위에다 돌을 올려 놓아서 열차 운행을 고의로 방해하거나 열차를 전복시키는 자는 무슨무슨 형에 처해진다는 경고문도 꼭 붙어 있었다.

우리나라에 발생했던 상당히 참혹한 건널목 사고로 철덕이라면 1970년의 장항선 모산 수학여행 참사를 기억할 것이다. 그건 아주 극단적인 예이다.
2002년 5월 1일에는 잘 알다시피 전라선 상행 새마을호에서 세계적으로 유례를 찾기 힘든 괴이한 3콤보 건널목 인명 사고가 난 적이 있다.

오늘날에도 전국에서 건널목 사고로 목숨을 잃는 사람이 연간 10~20여 명은 있는 모양이다. 이것도 물론 2, 30여 년 전의 100수십여 명에 비해서는 매우 크게 감소한 것이다.

비교적 최근인 2011년 7월 30일에는 좀 어처구니없는 사고가 났다. KTX가 건널목에서 제네시스 승용차와 충돌하여 여성 운전자가 목숨을 잃었는데.. 아니, 고속선에서 시속 300km로 질주하고 있을 KTX가 호남선 구간도 아닌 무슨 연기군에서 웬 건널목 사고에 연루되는 게 가능한지 궁금할 것이다. 이 KTX는 대전-서울 구간을 기존선으로 달리는 녀석이었다.

건널목을 건너던 중에 차가 시동 꺼져 뻗은 것도 아니고, 자세한 경위를 들어 보니 정말 “아 씨바 할 말을 잃었습니다” 급이다. 차가 건널목을 통과하고 있던 도중에 차단기가 내려와 버렸다.;; 그래서 그 차는 철길에 갇혀 고립됐다. 타이밍 한번 정말 더럽다. ㄷㄷㄷㄷ

경부선은 복선이기 때문에 철길의 폭과 양쪽 건널목 사이의 간격이 단선보다 훨씬 더 크다. 그래서 이런 케이스가 가능했다. 복선 건널목이 단선 건널목보다 더욱 위험하다는 건 두말 할 나위가 없다.

마치 고속도로 톨게이트에서 하이패스가 인식되지 않아서 차단봉이 내려오더라도 당황하지 말고 일단 통과는 해야 하듯, 이때도 차 범퍼로 차단기의 철길 안쪽면을 툭 치기만 하면 차단기는 다시 올라가게 돼 있다. 차단기는 위험을 알려서 사람을 살리려고 만들어진 장치이지 사람을 잡으려고 만들어진 게 아니다.

그랬는데, 지금은 이미 고인이 된 그 운전자는 당황하여 그대로 차를 세운 채 철길 위에서 아무 조치도 취하지 않았고, 하다못해 밖으로 탈출하지도 않았다. 마치 자신이 철길 밖의 건널목 앞에서 기다리고 있는 것처럼 그냥 가만히 있었다. 열차가 알아서 정지해 줄 거라고 생각했는가 보다.

충돌 직전 몇 초 동안 KTX 열차가 비상 제동을 걸면서 필사적으로 경적을 빵빵 울릴 때 그분, 무슨 생각을 했을까? 그 뒤 시밤쾅! 그 고급 승용차는 박살 난 채 전복되어 나뒹굴었다. 이건 뭐 스크린도어에 끼인 채로 열차가 출발해 버려서 사람이 죽은 것 같은 그런 기괴한 사고이다만, 더 근본적으로는 고인이 철도에 대해서 너무 무지하여 벌어진 참극이 아닐 수 없다.

우리나라 철도 당국은 사고의 원흉인 평면교차와 건널목을 없애려고 많은 노력을 기울이고 있다. 신호 받으면서 수시로 가다 서다 하기가 어려운 교통수단일수록 자기만의 독립된 길에서 쭉 가기만 하게 해 주는 게 당연히 유리하니까 말이다.

다만 철도가 너무 입체화만 되면 사람의 발이 철길과 직접 교감을 할 기회가 없어지니, 발을 어느 정도 뻗어야 표준궤 궤간인지 감을 익히기가 어렵다. 난 고향에 가면 철길 건널목에 도보로 들를 일이 있을 때 그거 연습을 하곤 한다.

.
.

그리고 또 여담 하나.

<다리>(Bridge)라고 2003년에 체코에서 만들어진 단편 독립영화가 있다. 원제목은 Most인데, 이건 영어의 형용사/부사를 뜻하는 most가 아니라, 자기네 체코 어로 bridge라는 뜻이다. 스토리는 대략 이렇다.

어느 강에 철교가 놓여 있는데, 이게 배가 지나갈 때는 공간 확보하려고 다리를 들어올리게 돼 있다. 다시 말해 이 다리는 승강교이며, 자동차가 아니라 배와 열차 사이의 평면교차로이다.
신호 지시에 따라 다리를 올리고 내리는 관리자가 있고, 그에게는 어린 외아들이 있다. 그런데 어느 열차의 기관사가 적색 정지 신호를 못 본 채, 다리가 열려 있는 철교 구간으로 열차를 진입시키고 만다. 그러고 보니 이 열차는 가감속의 매우 힘든 증기 기관차이며, 영화의 배경은 20세기 초중반으로 보인다.
다리 관리실 근처에서 놀고 있던 아들은 멀리서 이 열차를 보고 황급히 놀라지만, 아무리 소리를 질러도 배경 소음 때문에 아버지는 상황 파악을 못 한다. 아들은 열차가 끊어진 다리로 더 진입을 못 하게 하려고 선로 쪽으로 달려가다가 잘못해서 다리의 부품에 끼이고 만다.
아버지는 그제서야 모든 상황을 파악한다. 지금이라도 다리를 황급히 내리면 열차를 아주 간신히 다리 건너편으로 통과시킬 수는 있다. 하지만 다리가 내려가면서 부품에 끼여 있던 아들이 끔살 당하게 된다. 그 반면, 그 조치를 취하지 않으면 아무것도 모르는 열차는 끊어진 선로 너머 강으로 추락하고 수백 명의 승객들이 죽거나 다친다.
이때 애 아버지는 눈물을 머금고 결국 승객들을 살리는 길을 택한다. 다리를 내리는 스위치를 누른 후 절규한다. 열차 안의 승객들은 아무 일도 없었다는 듯 무심히 다리를 통과하는데, 예전에 굉장히 방탕하게 살던 한 여인만이, 이 일의 진실을 알게 된다. 그래서 예전의 방탕한 생활을 딱 끊고 결혼도 하고 바른 생활로 돌아오더라는 내용.


이 영화는 기독교적인 메시지를 의도하고 만들어졌다. 그 다리 관리인이 자기 아들을 희생시켜서 수백 명의 승객들을 살렸듯이 인간을 지으신 하나님도 자신의 아들을 십자가에 죽게 하셔서... 이하 생략이다.

영화 제작자의 의도는 알겠으나, 설정이 좀 억지스러운 면모가 있긴 하다. 오늘날 철도에 구비되어 있는 기본 중의 기본 시스템인 ATS 하나만 있어도 적색 신호를 무시하고 가는 열차는 얄짤없이 자동으로 멈춰 서게 된다. 하지만 저 때는 아예 증기 기관차가 다니던 시절이니 기관사의 시력과 재량이 철도 안전에 직접적인 영향을 끼칠 수도 있었겠다.

요즘은 그런 원시적인 후진국형 철도 사고는 근무 기강이 빠질 대로 다 빠진 막장이거나, 진짜 철도 인프라가 100년 전이나 지금이나 변한 게 없는 곳에서나 난다.
아무튼 철도는 디테일을 알면 알수록 더욱 재미있다.

Posted by 사무엘

2012/05/27 08:24 2012/05/27 08:24
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/687

C++11의 람다 함수

프로그래밍을 하다 보면, 어떤 컨테이너 자료구조의 내부에 있는 모든 원소들을 순회하면서 각 원소에 대해 뭔가 동일한 처리를 하고 싶은 때가 빈번히 발생한다.
그 절차를 추상화하기 위해 C++ 라이브러리에는 algorithm이라는 헤더에 for_each라는 템플릿 함수가 있다. 다음은 이 함수의 로직을 나타낸 C++ 코드이다. 딱히 로직이라 할 것도 없이 아주 직관적이고 간단하다.

template<typename T, typename F>
void For_Each_Counterfeit(T a, T b, F& c)
{
    for(T i=a; i!=b; ++i) c(*i);
}

C++은 템플릿과 연산자 오버로딩을 통해 자료구조에 대한 상당 수준의 추상화를 달성했다.
iterator에 해당하는 a와 b는 그렇다 치더라도, 여기서 핵심은 c이다.
F가 무엇인지는 모르겠지만, 어쨌든 c는 함수 호출 연산자 ()를 적용할 수가 있는 대상이어야 한다.
그럼 무엇이 가능할까? 여러 후보들이 있다.

일단 C언어라면 함수 포인터가 떠오를 것이다. 함수 포인터는 코드를 추상화하는 데 지금까지 고전적으로 쓰여 온 기법이다.

void foo(char p);

char t[]="Hello, world!";
For_Each_Counterfeit(t, t+strlen(t), foo);

C++에서는 클래스가 존재하는 덕분에 더 다양한 카드가 생겼다. 클래스가 자체적으로 함수 호출을 흉내 내는 연산자를 갖출 수 있기 때문이다.

class MyObject {
public:
    void operator()(char x);
};

For_Each_Counterfeit(t, t+strlen(t), MyObject());

그리고 더욱 기괴한 경우이지만, 클래스 자신이 함수의 포인터로 형변환이 가능해도 된다.

class MyObject {
public:
    typedef void (*FUNC)(char);
    operator FUNC();
};

For_Each_Counterfeit(t, t+strlen(t), MyObject());

C++ 라이브러리에는 functor 등 다양한 개념들이 존재하지만, 그 밑바닥은 결국은 C++ 언어의 이런 특성들을 사용해서 구현되어 있다.
여기서 재미있는 점이 있다. 다른 자료형과는 달리 함수 포인터로 형변환하는 연산자 오버로드 함수는, 자신이 가리키는 함수의 prototype을 typedef로 미리 만들어 놓고 반드시 typedef된 명칭으로 선언되어야 한다는 제약이 있다. 이것은 C++ 표준에도 공식적으로 명시되어 있는 제약이라 한다.

이런 어정쩡한 제약이 존재하는 이유는 아마도 함수 선언문에다가 다른 함수를 선언하는 문법까지 덧붙이려다 보니, 토큰의 나열이 너무 지저분해지고 컴파일러를 만들기도 힘들어서인 것 같다. 이 부분에서는 아마 C++ 위원회에서도 꽤 고민을 하지 않았을까.
안 그랬으면 형변환 연산자 함수의 prototype은 아래와 비슷한 괴상한 모양이 됐을 것이다. 실제로 이 함수의 full name을 undecorate한 결과는 이것처럼 나온다.

    operator void (*)(char)();

비주얼 C++에서는 함수를 저렇게 선언하면 그냥 * 부분에서 '문법 에러'라는 불친절한 말만 반복할 뿐이지만, xcode에 기본 내장되어 있는 최신형 llvm 컴파일러는 놀랍게도 나의 의도를 간파하더이다. “함수 포인터로 형변환하려면 반드시 typedef를 써야 합니다”라는 권고를 딱 하는 걸 보고 적지 않게 놀랐다. 이런 차이도 맥북을 안 쓰고 오로지 비주얼 C++ 안에서만 틀어박혀 지냈다면 경험하기 쉽지 않았을 것이다. 우왕~

() 연산자 오버로딩은 this 포인터가 존재하는 C++ 클래스 멤버 함수이며 static 형태가 있을 수 없다.
그러나 함수 포인터로의 형변환 연산자 오버로딩은 this가 없으며 C 스타일의 static 함수와 같은 위상이라는 차이가 존재한다.
두 오버로딩이 모두 존재하면 어떻게 될까? 혹시 모호성 오류라도 나는 걸까?

그런 개체에 함수 호출 ()가 적용되는 경우, () 연산자가 먼저 선택되며, 그게 없을 때에 한해서 함수 포인터 형변환이 차선책으로 선택된다. 모호성 오류가 나지는 않는다.
포인터 형변환 연산자와 [] 연산자가 같이 있을 때 개체에 배열 첨자 참조 []가 적용되는 경우, 역시 [] 가 먼저 선택되고 그게 없을 때 포인터 형변환이 차선으로 선택되는 것과 비슷한 맥락이라 볼 수 있다.

그래서 클래스와 연산자 오버로딩 덕분에 저런 문법이 가능해졌는데, C++11에서는 그걸로도 모자라 또 새로운 문법이 추가되었다. 이른바 람다 함수.

For_Each_Counterfeit(t, t+strlen(t), [](char x) { /* TODO: add your code here */ } );

람다 함수는 코드가 들어가야 할 곳에 함수나 클래스의 작명 따위를 신경쓰지 않고 코드 자체만을 직관적으로 곧장 집어넣기 위해 고안되었다. 세상에 C++에서 OCaml 같은 데서나 볼 수 있을 법한 개념이 들어가는 날이 오다니, 신기하지 않은가?

덕분에 C++은 C언어 같은 저수준 하드웨어 지향성에다가 성능과 이념을 적당히 절충한 수준의 객체지향을 가미했고, 90년대 중반에는 템플릿 메타프로그래밍 개념을 집어넣더니, 이제는 함수형 언어의 개념까지 맛보기로 도입한 가히 멀티 패러다임 짬뽕 언어가 되었다.

함수를 값처럼 표현하기 위해서 lambda 같은 예약어가 별도로 추가된 게 아니다. C/C++은 태생상 예약어를 함부로 추가하는 걸 별로 안 좋아하는 언어이다. (그 대신 문법에 혼동이 생기지 않는 한도 내에서 기호 짬뽕을 좋아하며 그래서 사람이나 컴파일러나 코드를 파싱하는 난이도도 덩달아 상승-_-) 보아하니 타입을 선언하는 부분에서는 배열 첨자가 먼저 오는 일이 결코 없기 때문에 []를 람다 함수 선언부로 사용했다.

람다 함수는 다른 변수에 대입되어서 두고두고 재활용이 가능하다. 그래서 C/C++에서는 전통적으로 가능하지 않은 걸로 여겨지는 함수 내부에서의 함수 중첩 선언을 이걸로 대체할 수 있다.

어떤 함수 안에서 특정 코드가 반복적으로 쓰이긴 하지만 별도의 함수로 떼어내기는 싫을 때가 있다. 굳이 함수 호출 오버헤드 때문이 아니더라도, 해당 코드가 그 함수 내부의 지역변수를 많이 쓰기 때문에 그걸 일일이 함수의 매개변수로 떼어내기가 귀찮아서 그런 것일 수도 있다.
이때 흔히 사용되는 방법은 그냥 #define 매크로 함수밖에 없었는데 이때도 람다 함수가 더 깔끔하고 좋은 해결책이 될 수 있다. 람다 함수는 선언할 때 캡처라 하여 주변의 다른 변수들을 참조하는 메커니즘도 언어 차원에서 제공하기 때문이다.

그렇다면 의문이 생긴다.
람다 함수는 그럼 완전히 새로운 type인가?
기존 C/C++에 존재하는 함수 포인터와는 어떤 관계일까?
정답부터 말하자면 이렇다. 람다 함수는 비록 어쩌다 보니 () 연산을 받아 주고 함수 포인터가 하는 일과 비슷한 일을 하게 됐지만, 활용 형태는 함수 포인터하고 아무 관련이 없으며 그보다 더 상위 계층의 개념이다.

템플릿과 연동해서 쓰인다는 점에서 알 수 있듯, 람다 함수는 함수 포인터와는 달리 calling convension (_stdcall, _cdecl, _pascal 나부랭이 기억하시는가?)이고 리턴값이 나발이고간에 아무 상관이 없다. 그저 코드상에서 함수를 값처럼 다루는 걸 돕기 위해 존재하는 추상적인 개념일 뿐이다. 뭔가 새로운 type이 아니기 때문에 람다 함수를 변수에다 지정할 때는 auto만을 쓸 수 있다. 즉, 다른 자료형이 아닌 람다에 대해서는 auto가 선택이 아니라 필수라는 뜻이다.

auto square=[](int x) { return x*x; };
int n = square(9); //81

square는 템플릿 같은 함수가 아니다. 이 함수의 리턴값은 x*x로부터 자동으로 int라고 유추되었을 뿐이다. [](int x) -> int 라고 명시적으로 리턴 타입을 지정해 줄 수도 있다. 구조체 포인터의 멤버 참조 연산자이던 -> 가 여기서 또 화려하게 변신을 한 셈임. 우와!

또한, sizeof(square)을 한다고 해서 포인터의 크기가 나오는 게 아니다. 사실, 람다 함수에다가 sizeof를 하는 건 void에다가 sizeof를 하는 것만큼이나 에러가 나와야 정상이 아닌가 싶다. 그런 개념하고는 아무 관계가 없기 때문이다.

람다는 함수 포인터가 아니기 때문에, square에다가 자신과 프로토타입이 같은 다른 람다 함수를 대입할 수 있는 건 아니다. 함수 포인터의 역할과 개념을 대체할 뿐, 그 직접적인 디테일한 기능을 대체하지는 못한다. 그렇기 때문에 콜백 함수를 받는 문맥에서

qsort(n, arrsize, sizeof(int), [](const void *a, const void *b) { return *((int*)a) - *((int*)b); } );

구닥다리 C 함수에다가 최신 C++11 문법이라니, 내가 생각해도 정말 변태 같은 극단적인 예이다만,
이런 식으로 람다 함수를 집어넣을 수도 없다.

요컨대 람다 함수는 코드의 추상화에 도움을 주고 종전의 함수 포인터 내지 #define, 콜백 함수 등의 역할을 대체할 수 있는 획기적인 개념이다. C++ 철학대로 디자인된 여타 C++ 라이브러리와 함께 사용하면 굉장한 활용 가능성이 있다. 그러나 이것은 함수 포인터에 대한 syntatic sugar는 절대 아니라는 걸 유의하면 되겠다.

Posted by 사무엘

2012/05/24 08:38 2012/05/24 08:38
, , ,
Response
No Trackback , 15 Comments
RSS :
http://moogi.new21.org/tc/rss/response/686

나의 신앙 자가진단

※ 나는 왜 예수님을 믿는가 -- 크게 작용한 요인들
  • 세상 그 어느 종교도 창조주가 피조물에게 죽임을 당했다고 가르치지 않으며, 교주가 부활했다고 가르치지 않고, 또 이 정도로 역사적으로 방대한 증거와 증인들을 갖추고 있지 않으므로
  • 인간이 자기 노력과 근성으로 신을 찾아가는 게 아니라, 신이 먼저 인간을 찾아주고 은혜와 사랑을 베풀었다고 가르치므로
  • 없어졌으면 애시당초 진작에 씨가 말라 버렸을 정도로 황당하고 믿어지지 않는 교리를 갖고 있는데, 아직까지 당당히 존속해 있다는 것만으로도 최소한 무시할 수는 없고 한번 살펴볼 가치가 있다고 여겨졌으므로
  • 이 정도로 무수히 많은 이단들이 압도적으로 집착할 정도이면, 웬지 이 바닥에 분명 진리가 있을 거라는 예감이 들어서
  • 성경은 그 논조와 내용을 볼 때 인간이 쓸 만한 책이 절대 아니라는 확신이 들어서 (가령, 정치적으로 치우침이 없음. 인간 자신에게 절대 이롭지 않은 내용이 지나치게 장황하게-_- 많이 들어있음)
  • 그래도 몇몇 증명 불가능하고 이해가 안 되는 사항들만 일단 믿고 나면, 이를 바탕으로 전개되는 각종 교리와 윤리관은 아주 논리적이고 이성적이고 합리적이고 인간에게 건전하다는 게 너무 분명하게 확 느껴져서
  • 죄 문제라는 인간에 대한 상태 진단과, 그 해결책에 너무나 공감이 가서. 최소한 줘도 못 먹는 사람이 되지는 말아야지?
  • 차라리 예수 그리스도라는 절대적인 의의 기준이 온갖 상대주의· 다원주의보다는 훨씬 더 명확하고 깔끔하고 건전하고 뒤끝이 없다는 생각이 들어서 (다 사회 구조 탓이다, 그 상황에서는 누구나 그럴 수밖에 없다 등등)

그래서 내가 지금 어떻게 되었는지에 대해서는 이 글을 참고할 것.

※ 나는 왜 예수님을 믿는가 -- 조금 작용한 요인들

  • 개독안티들의 무례하고 표독스러운 말투에, 사실 여부를 떠나 괜히 반발심과 환멸을 느껴서 (다른 건 몰라도 저놈들 말은 절대로 듣지 말아야겠다는 식)
  • 역사적으로 기독교의 과오라고 알려진 것들이 상당수가 기독교와는 아무 관계가 없고, 사실은 크리스천들도 오히려 피해자라는 걸 알게 되어서
  • 파스칼의 팡세에 나오는 수준의 간단한 변증론. 가령,
    “지금 예수 믿었는데 나중에 알고 보니 사실 없는 게, 지금 안 믿었는데 진짜로 지옥이 있어서 낭패 보는 것보다 더 안전하고 리스크가 적다.”
    “신이 없다고 단정짓기엔 인간의 지식은 너무 좁고 빈약하다” 같은 식.
  • 내세와 심판이 있을 거라는 양심의 자극. 죽음에 대한 두려움
  • 성경이 과학적으로도 옳다는 걸 뒷받침하는 몇몇 자료들
  • 이 정도 교리면, 정말 만에 하나 성경의 내용이 다 거짓이고 허구이고 설령 근거 없이 맹목적으로 믿는다 해도, 크게 손해 볼 게 없다고 생각되어서. 성경은 최소한 날 골탕먹이려고가 아니라 날 '위해서' 기록된 책이라는 느낌이 와서

※ 내 신앙관에 영향을 거의 끼치지 않은 것들

  • 좋든  싫든, 주변 교회 사람들의 행동과 평판
  • 잘한 것이든 못한 것이든, 해당 종교계에서 유명한 사람들의 언행 (그 사람들이랑 나랑 도대체 무슨 상관이냐? 나는 베르테르 효과 같은 것과는 담을 쌓고 지냄)
  • 세상 불신자들로부터의 평판, 매스미디어에 묘사된 이미지
  • 육신을 들뜨게 하거나 흥분시키거나 만족시키는 종교심. 나는 그런 부류의 종교심은 이미 철도교로 다 충족하고 있기 때문에 그런 종교에다 내 영원을 걸지는 않는다.
  • 기복신앙

하지만 현실에서는 저런 것들을 보고 교회를 나가거나 종교를 선택하는 사람들이 무진장 많다. ㅜㅜ

※ 지금은 발현되지 않고 있지만, 언젠가 깊은 시험에 들고 신앙 면역 체계가 무너졌을 때 큰 위험이 될 수 있는 잠재적인 암적 요소들

  • 성경에서 여전히 잘 이해되지 않고 해결이 안 된 의문이나 논리적 모순(처럼 보이는 것들) 몇 군데. 목사님에게 여쭈거나 주석서를 봐도 알 수 없는 것들
  • 성경이 밥 먹여 주냐... 같은 부류의 유치하지만, 좁은 길을 가는 성도에게 결코 무시할 수 없는 시험. 현실의 염려 (눅 8:14)
  • 신앙생활이 매너리즘으로 변질돼 가는 것
  • 하나님의 뜻을 도무지 알 수 없을 때. 이것도 하지 말고 저것도 안 하면 도대체 뭘 하라는 건지 알 수 없는 상황이 들이닥치는 것

Posted by 사무엘

2012/05/21 19:28 2012/05/21 19:28
, , , ,
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/685

1.

옛날에 폰 노이만(폰 노이만 구조라는 컴퓨터 근간을 닦은 사람)이라는 사람은 기계어로 직접 컴퓨터에다 코딩을 하는 기계어 매니아였다. 기계어가 너무 불편하다고 어느 제자가 어셈블리 비슷한 상위 계층 언어를 만들려 하자 “귀한 컴퓨터 자원으로 쓸데없는 짓이나 한다”고 그를 나무랐다.;;
이거 마치 희대의 저격수인 시모 하이하가 조준경 그딴 걸 왜 쓰냐고 나무란 것과 비슷한 맥락 같다.;;
 
그 반면, 데이크스트라(다익스트라. 그래프 탐색 알고리즘을 고안한 그 사람)는 어셈블리/기계어 같은 언어를 비생산적이고 삽질스럽다고 아주 강하게 디스한 것으로 잘 알려져 있다. 그도 그럴 것이, 구조화 프로그래밍을 주장하면서 GOTO문을 배격한 사람이 기계스러운 BRANCH 따위가 난무하는 저급 언어를 좋아할 리가 없겠다.
 
둘 다 우주괴수급의 천재 수학자 및 전산학자이다만, 이런 식의 관점의 차이가 존재하는가 보다. 재미있는 일이다.
 
2.

밸브 코퍼레이션의 창립자 게이브 뉴웰 (카운터 스트라이크, 하프 라이프, 포탈 등의 게임 개발사)
페이스북의 창립자인 마크 주커버그 (나보다 더 어림..)
마이크로소프트의 창립자인 빌 게이츠 (설명 불필요)
 
억만장자 IT 기업인인 이들은 모두 하버드 대학 중퇴자라는 공통점이 있다. 다 미국인이기도 하고.

3.

개발자가 소프트웨어를 팔아서 먹고 살려면

(1) 관공서나 기업에서 구입하지 않을 수 없는 핵심적인 프로그램을 개발 (교육이나 업무 분야)
(2) 하드웨어에 같이 들어가는 프로그램을 개발해서 하드웨어와 함께 판매
(3) 온라인 게임 개발 (늘 서버 접속을 하기 때문에 이용료 징수 가능)
(4) 아니면 개인을 대상으로도 유료 판매가 가능한 유통 경로(앱스토어, 스마트폰 등)를 거치는 프로그램 개발

중 하나로는 가야 할 것 같다. 저 네 가지 말고 혹시 다른 방법이 있을까?

4.

내가 맥 OS에 매력을 느끼는 큰 이유 중 하나는.. 폰트 래스터라이저가 정말 짱이라는 점.
똑같은 글꼴을 화면에 찍어 내는 퀄리티가 서로 게임이 안 되는 수준이니...

사용자 삽입 이미지
위는 Windows, 아래는 맥이다.
Windows는 ClearType을 시키면 맑은 고딕처럼 전용 힌팅이 들어간 글꼴이 아니면 그냥 안티알리어싱이 없는 것보다 약간 나은 정도로만 찍히는 반면.
Mac은 힌팅이 없다시피한 글꼴도 Adobe Reader 이상의 퀄리티로 찍어 준다!

5.

그나저나 맥 OS는 Finder (윈도우로 치면 탐색기)에서 파일이나 디렉터리의 이름을 바꾸는 게 엔터이고, 실행하거나 여는 게 Cmd+아래라니 참 희한하다. 윈도우라면 이름 바꾸는 건 F2이고, 여는 게 응당 엔터인데 말이다.

6.

과거에 MS 오피스가 2003에서 2007로 버전업되었을 때 비주얼이 화려해지고 좋아진 기능이 분명 적지 않았지만, 내게는 굉장히 마음에 안 드는 변화도 있었다. 그것 중 하나는 파워포인트에서 '컬러 타자기' 애니메이션 효과가 굉장히 느려져서 랙이 심해지고 프레임 수가 감소한 것이었다. 글자가 말 그대로 타자기로 찍듯이 한 글자씩 천천히 나타나는 것 말이다. 그렇게 현란하거나 CPU의 부하가 심한 효과도 아니다.

그랬는데 2010을 나중에 써 보니, 마치 2003처럼 애니메이션이 다시 매끄러워져 있었다.
혹시 컴퓨터가 빨라지고 화면 해상도가 낮아져서 그런 게 아닌가 싶어서 컴퓨터를 바꿔서 확인해 보았다.
그랬더니 같은 1280*1024 화면이라도 역시 2010에서는 Core2 duo급 컴에서도 매끄럽게 나오는 반면, 2007에서는 쿼드코어 i5급 컴에서도 버벅거렸다.

그래서 이것은 소프트웨어적인 알고리즘 개선 덕분이라는 결론을 내리게 됐다. 2007과 2010 사이엔 이런 차이도 존재하는가 보다.

7.

근래엔 <날개셋> 한글 입력기의 구성 파일들에 대해서 바이러스 및 악성 코드 진단 문의가 부쩍 늘었다. 그래서 그에 대한 개발자의 공식 입장을 내 홈페이지에다가도 게시할 필요를 느끼게 됐다.

결론부터 말하자면 당연히 “바이러스 아님”이다. 모든 프로그램들은 바이러스도, 안티바이러스(일명 백신)도 알지 못하는 100% 청정 컴퓨터에서 개발되며, 개발 환경에서 갓 빌드된 직후의 실행 파일들이 곧바로 설치 패키지로 포장된다. 바이러스 같은 게 들어갈 일이란 없다. 이 일 때문에 본인에게 문의하면 언제나 동일한 대답밖에 돌아올 게 없으며, 그 외에 더 할 말이 없음을 이 자리에서 밝히는 바이다.

더 근본적으로는 실행 파일과 MSI 패키지가 디지털 서명을 받지 못한 관계로, 웹브라우저부터가 빨간 경고와 함께 <날개셋> 프로그램의 다운로드를 저지(discourage)하는 것도 좀 아쉬운 점이다. 이건 훗날 프로그램이 더 나은 수익원과 배포 통로를 확보했을 때에나 해결 가능할 것 같다.

그래서 이 참에 아예 프로그램 다운로드 페이지에다가 설명을 써 놨다. “10년이 넘게 인생을 걸며 이 프로그램을 개발하고 개선해 온 저를 믿으신다면, 그런 보안 경고들은 모두 무시하고 안심하고 사용하시기 바랍니다.

문득 생각해 볼 문제: 비주얼 C++이나 그에 상응하는 개발툴이 설치된 컴퓨터를 자동으로 감지하여 프로그램이 링크될 때 쓰이는 C 라이브러리 같은 lib, obj 파일을 감염시키는 컴퓨터 바이러스 프로그램이 존재할까? 처음부터 바이러스에 감염된 프로그램이 생성되도록? -_-;;

Posted by 사무엘

2012/05/19 08:22 2012/05/19 08:22
, ,
Response
No Trackback , 9 Comments
RSS :
http://moogi.new21.org/tc/rss/response/684

지금은 C++11이라고 개명된 C++ 확장 규격인 C++0x에는 잘 알다시피 여러 참신한 프로그래밍 요소들이 추가되었다. 몇 가지 예를 들면, 상당한 타이핑 수고를 덜어 줄 걸로 예상되는 auto 리뉴얼, 숫자와 포인터 사이의 모호성을 해소한 nullptr, 그리고 숫자와 enum 사이의 모호성을 해소한 enum class가 있다.

그런데 이것 말고도 C++11에는 아주 심오하고도 재미있는 개념이 하나 또 추가되었다. 복사 생성자에 이은 이동 생성자, 그리고 이를 지원하기 위한 type modifier인 &&이다. R-value 참조자라고 불린다. 이 글에서는 이것이 왜 도입되었는지를 실질적인 코드를 예를 들면서 설명하겠다.
다음은 생성자에서 주어진 문자열의 복사본을 보관하는 일만 하는 아주 간단한 클래스이다.

//typedef const char*  PCSTR;
class MyObject {
    PCSTR dat;
public:
    MyObject(PCSTR s): dat(strdup(s)) {}
    ~MyObject() { free( const_cast<PSTR>(dat) ); }
    operator PCSTR() const { return dat; }
};

C++은 언어 차원에서 포인터를 자동으로 관리해 주는 게 전혀 없다. 그렇기 때문에 저렇게만 달랑 짜 놓은 클래스는 함부로 값을 대입하거나 함수 호출 때 개체를 reference가 아닌 value로 넘겨 줬다간, 동일 메모리의 다중 해제 때문에 프로그램이 jot망하게 된다. C++ 프로그래머라면 누구라도 위의 코드의 문제를 즉시 알 수 있을 것이다.

그렇기 때문에, 포인터처럼 외부 자원을 따로 가리키는 클래스는 복사 생성자와 대입 연산자를 별도로 구현해 줘야 한다. 구현을 안 할 거면 하다못해 해당 함수들을 빈 껍데기만 private 형태로 정의해서 접근이 되지 않게 해 놓기라도 해야 안전하다.

MyObject(const MyObject& s): dat(strdup(s))
{
    puts("복사 생성자");
}
MyObject& operator=(const MyObject& s)
{
    free(dat); dat=strdup(s.dat); puts("복사 대입");
    return *this;
}

자, 그럼 이를 이용해 그 이름도 유명한 Swap 루틴을 구현해서 복사 생성자와 대입 연산자를 테스트해 보자.

template<typename T>
void Swap(T& a, T& b) { T c(a); a=b; b=c; }

int main()
{
    MyObject a("새마을호"), b("무궁화호");
    printf("%s(%X) %s(%X)\n", (PCSTR)a,(PCSTR)a, (PCSTR)b,(PCSTR)b);
    Swap(a,b);
    printf("%s(%X) %s(%X)\n", (PCSTR)a,(PCSTR)a, (PCSTR)b,(PCSTR)b);
    return 0;
}

프로그램의 실행 결과는 다음과 같은 식으로 나올 것이다.

새마을호(181380) 무궁화호(181390)
복사 생성자
복사 대입
복사 대입
무궁화호(1813B8) 새마을호(1813D0)

복사 생성자와 대입 연산자 덕분에 메모리 관리는 옳게 되었기 때문에, 이제 프로그램이 뻗는다거나 하지는 않는다.
그러나 이 방법은 비효율적인 면모가 있다. 개체의 값을 맞바꾸기 위한 세 번의 연산 작업 동안, 당연한 말이지만 메모리 할당과 해제, 그리고 문자열의 복사가 매번 발생했다. 그래서 비록 문자열 값은 동일하지만 그 문자열이 담긴 메모리 주소는 a와 b 모두 예전과는 완전히 다른 곳으로 바뀌었음을 알 수 있다.

이때 R-value 참조자를 쓰면, 이 클래스에 대해서 Swap 연산이 메모리를 일일이 재할당· 복사· 해제하는 게 아니라 a와 b가 가리키는 문자열 메모리 주소만 간편하게 맞바꾸도록 하는 언어적인 근간을 마련할 수 있다. 기존 참조자는 &로 표현하고, 이와 구분하기 위해 R-value 참조자는 &&로 표현된다. 참조자(&)는 포인터(*)와는 달리 다중 참조자(참조자의 참조자) 같은 개념이 없기 때문에, &&을 이런 식으로 활용해도 문법에 모호성이 생기지 않는다.

& 대신 &&를 이용해서 자신과 동일한 타입의 개체를 받아들이는 생성자와 대입 연산자를 추가로 정의할 수 있다. 이 경우, 이들 함수는 복사가 아닌 이동 생성자와 이동 대입 함수가 된다. 아래의 예를 보라.

MyObject(MyObject&& s)
{
    dat=s.dat, s.dat=NULL; puts("이동 생성자");
}
MyObject& operator=(MyObject&& s)
{
    //주의: 실제 코드라면 자기 자신에다가 대입하는 건 아닌지 체크하는
    //로직이 추가되어야 한다. if(&s!=this)일 때만 수행하도록.
    free(dat); dat=s.dat, s.dat=NULL; puts("이동 대입");
    return *this;
}

복사 버전과는 달리, strdup 함수 대신 그냥 포인터 대입을 썼음을 알 수 있다. 이것이 핵심이다.
그러면 s가 가리키던 메모리 영역이 내 것이 된다. 그 뒤 s가 가리키던 메모리는 NULL로 없애 줘야 한다. free 함수는 그 스펙상 자체적으로 NULL 체크를 하기 때문에, 소멸자 함수는 그대로 놔 둬도 된다.

즉, 이동 생성자와 이동 대입은 s의 값을 내 것으로 설정하긴 하나, 그 과정에서 필요하다면 s의 내부 상태를 건드려서 바꿔 놓을 수 있다. 그렇기 때문에 복사 생성자/대입과는 달리 s가 const 타입이 아니다.

이것만 선언해 줬다고 해서 Swap 함수의 동작 방식이 이동 연산으로 곧장 바뀌는 건 물론 아니다. 그랬다간 s의 상태가 바뀌고 프로그램 로직이 달라져 버리기 때문에, 컴파일러가 섣불리 동작을 바꿀 수 없다. 그렇기 때문에 Swap 함수의 코드도 move-aware하게 살짝 고쳐야 한다.

template<typename T>
void Swap(T& a, T& b)
{
    T c(static_cast<T&&>(a)); a=static_cast<T&&>(b); b=static_cast<T&&>(c);
}

즉, 개체를 생성하고 대입하는 곳에서, 가져오는 개체를 가능한 한 move로 취급하라고 명시적인 형변환을 해 줘야 한다. 이렇게 해 주고 나면 드디어 우리의 목표가 이뤄진다!

새마을호(181380) 무궁화호(181390)
이동 생성자
이동 대입
이동 대입
무궁화호(181390) 새마을호(181380)

물론, 저런 형변환 연산이 보기 싫은 사람은 <vector>에 정의되어 있는 std::move 함수로 이동 대입을 해도 되며, 보통 R-value 참조자를 설명해 놓은 인터넷 사이트들도 그 함수를 곧장 소개하고 있다. 하지만 그 함수의 언어적인 근거가 바로 이 문법이라는 건 알 필요가 있다.

생성이나 대입에서 R-value 참조자를 받지 않고 기존의 L-value 참조자만 받는 클래스에 대해서는, 이동 대입이나 생성도 자동으로 옛날처럼 복사 대입이나 생성 방식으로 행해진다.
다시 말해, Swap 함수의 로직을 저렇게 고치더라도 R-value 참조자가 구현되어 있지 않은 기존 타입들에 대한 동작은 전혀 바뀌지 않으며 컴파일 에러 같은 게 나지도 않는다. 그러니 호환성 걱정은 할 필요가 없다.

그리고 이미 눈치챈 분도 있겠지만, MFC의 CString처럼 자기가 가리키는 메모리에 대해서 자체적으로 reference counting을 하고 copy-on-modify 같은 테크닉을 구현해 놓았기 때문에, 어차피 복사 생성이나 call by value 때 무식한 오버헤드가 발생하지 않는 클래스라면, 구태여 이동 생성자나 이동 대입 연산자를 또 구현할 필요가 없다. 이동 생성/대입은 언제까지나 기존의 복사 생성/대입을 보조하기 위해서 도입되었기 때문이다.

특히 std::vector 같은 배열 컨테이너 클래스에다가 덩치 큰 개체를 집어넣거나 뺄 때 복사 생성자가 쓸데없는 오버헤드를 발생시키는 걸 막는 게 이 문법의 주 목적이다. 그렇기 때문에 딱히 smart한 복사 메커니즘을 갖추고 있지 않은 클래스를 STL 컨테이너에다 집어넣고 쓰는 C++ 코드라면, 적절한 이동 생성자와 대입 연산자를 구현해 주고 R-value 참조자를 지원하는 최신 C++ 컴파일러로 다시 빌드를 하는 것만으로도 성능 향상을 경험할 수 있다.

예전에는 배열 컨테이너 클래스들이 원소들의 일괄 삽입이나 삭제를 위해 무식한 memmove 함수를 내부적으로 쓰는 게 불가피했는데 이 역할을 이동 대입이 어느 정도 대체도 할 수 있게 됐다.
&&을 DLL symbol로 표기하기 위한 새로운 C++ type decoration도 별도로 물론 있다.

그런데 의문이 생긴다. &&의 이름이 왜 R-value 참조자인 것일까?
이 참조자는 참조자이긴 하지만, 오리지널 참조자처럼 L-value가 아니라 R-value를 취급하라고 만들어졌기 때문이다. L-value, R-value란 무엇인가? 대입문에서 좌변과 우변을 뜻한다. L-value란 값을 갖고 있으면서 동시에 대입의 대상이 될 수 있는 변수를 가리키며, R-value는 값을 표현할 수만 있지 그 자신이 다른 값으로 바뀔 수는 없는 상수, 혹은 임시 개체를 가리킨다고 보면 얼추 맞다.

아래의 코드에서 볼 수 있듯 기존 L-value 참조자는 dereference된 포인터와 같은 역할을 한다.

int& GetValue() { … }
GetValue() = 100;

int *GetValue() { … }
*GetValue() = 100;

그렇기 때문에 아래와 같은 특성도 존재한다.

void GetValue2(int& x) { x=… }

int a;
GetValue2(a); //a는 L-value이므로 OK
GetValue2(500); //에러. 당연한 귀결임

L-value 참조자가 상수값 내지 임시 생성 개체 같은 R-value를 함수의 인자로 받아들이려면, 해당 참조자는 const로 선언되어서 값의 변경이 함수 내부에서 발생하지 않는다는 보장이 되어야 한다. int&가 아니라 const int&로 말이다.

그런데 R-value 참조자는 const 속성 없이도 임시 개체나 상수값을 받아들이며, 그걸 뒤끝 없이 자유롭게 고칠 수 있다. 위의 GetValue2 함수가 int&&로 선언되었다면, 반대로 a를 전달한 게 에러가 나고 500을 전달한 건 괜찮다. a를 전달하려면 static_cast<int&&>(a)로 형변환을 해 줘야 한다. 그러면 마치 int&인 것처럼 실행되긴 한다.

R-value 참조자로 돌아온 함수의 리턴값은 말 그대로 R-value이기 때문에 대입 가능하지 않다. 그렇기 때문에 아래의 코드는 에러를 일으킨다. (R-value 참조자의 리턴값은 당연히 그 역시 R-value로 왔을 때에만 의미가 있을 것이다.)

int&& GetValue3() { … }
GetValue3() = 100; //에러

이런 R-value 참조자라는 괴상망측한 개념은 왜 도입된 것일까? 그리고 이게 앞서 이 글에서 언급한 이동 생성자/대입 연산하고는 도대체 무슨 관련이 있는 것일까?

R-value 참조자의 형태로 함수 인자로 넘어온 개체는 그 함수의 실행이 끝난 뒤엔 어차피 소멸되고 없어질 것이기 때문에 내부가 바뀌어도 상관없다. 즉, 이 참조자는 태생적으로 const 속성과는 어울리지 않는다. 오히려 const-ness가 보장되지 않아도 되는 제한적인 문맥에서, 쓸데없는 복사를 할 필요 없이 꼼수를 좀 더 합법적으로 구사할 수 있게 위해 이런 문법이 추가되었다고 보는 게 타당하다.

마지막으로 R-value 참조자가 유용하게 쓰이는 용도를 딱 하나만 더 소개하고 글을 맺겠다.
윈도우 API+MFC 기준으로, RECT 구조체를 받아서 이 값을 적당히 변형한 뒤에 이를 토대로 후처리를 하는 함수를 생각해 보자.

void Foo(const RECT& rc)
{
    RECT rc2 = rc; //rc는 const이기 때문에 복사본을 만들어야 함

    ::OffsetRect(&rc2, x,y); //변형
    ::DrawText(hDC, strMsg, -1, &rc2, 0);
}

void Foo(RECT&& rc)
{
    ::OffsetRect(&rc, x,y); //복사본 만들 필요 없이 rc를 곧바로 고쳐서 사용하면 됨
    ::DrawText(hDC, strMsg, -1, &rc, 0);
}

CRect r(100, 200, 400, 350);
Foo(r); //const RECT& 버전이 호출됨
Foo( CRect(0,0, 400,300) ); //임시 개체임. RECT&& 버전이 호출됨

RECT를 value로 전달했다면 당연히 복사가 일어나고, const reference로 전달했다면 역시 복사가 행해져야 한다. 그러나 애초에 함수에 전달되는 인자가 임시 개체였다면, 임시 개체에 대한 복사본을 또 만들 필요 없이 그냥 그 임시 개체를 바로 고쳐 쓰면 된다. 위의 코드의 의미가 이해가 되시겠는가?

R-value 참조자라는 게 왜 필요한지, 그리고 이게 왜 이동 생성/대입과 관계가 있는지 본인은 이해하는 데 굉장히 긴 시간이 걸렸다. 인터넷에 올라와 있는 다른 설명만 읽어서는 도통 이해가 되지 않아서 직접 코드를 돌리고 컴파일을 해 본 뒤에야 개념을 깨우쳤는데, 알고 나니 정말 이런 걸 생각해 낸 사람들은 천재라는 생각이 든다.;; C++은 참으로 복잡미묘한 언어이다.

Posted by 사무엘

2012/05/16 08:41 2012/05/16 08:41
,
Response
No Trackback , 15 Comments
RSS :
http://moogi.new21.org/tc/rss/response/683

« Previous : 1 : ... 158 : 159 : 160 : 161 : 162 : 163 : 164 : 165 : 166 : ... 221 : Next »

블로그 이미지

그런즉 이제 애호박, 단호박, 늙은호박 이 셋은 항상 있으나, 그 중에 제일은 늙은호박이니라.

- 사무엘

Archives

Authors

  1. 사무엘

Calendar

«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        

Site Stats

Total hits:
3066736
Today:
259
Yesterday:
1788