유시민의 글쓰기 특강(유시민, 2015)


   나에게 유시민은 작가라기 보다, 참여정부의 정치인으로서 더 익숙하다. 노무현 대통령 탄핵  표결 당시 분루를 곱씹던 그 날카로운 눈매나, 첫 국회 연설 때 비지니스 캐쥬얼을 입고 나와 욕을 먹던 모습 등이 기억난다.
   하지만 유시민은 정치인 이전에 '거꾸로 읽는 세계사' 등을 집필한 작가이다. 그가 처음 세상에 이름을 알린 것은 '서울대 프락치 사건'의 1심 판결 이후 작성한 '항소 이유서'를 통해서이다. 학생 뿐 아니라 심지어 판사들까지 돌려 읽어볼 정도로 날카로움과 절절함을 담고있는 글이었다. 

   이 책은 유시민 작가가 본인의 글쓰기 노하우를 담아낸 책이다. 글의 큰 틀 부터, 문장, 단어까지 세세히 짚어가며 본인이 생각하는 좋은 글에 대해 논한다.
   기억나는 것 중 가장 인상적인 것은, '취향과 주장'에 관한 내용이었다. 취향은 논쟁거리가 아니며 주장은 그를 뒷받침할 논증을 반드시 해야한다는 내용이다. 최근 인터넷을 돌아다니는 글, 심지어 신문 사설마저도 이 내용을 지키지못한 경우가 많다. 찬찬히 읽고있노라면 저자의 분풀이말고는 어떤 내용도 없는 경우가 있고, 그렇다보니 읽는 시간이 아까운 경우가 부지기수다.
   다음은 어려운 글이 좋은 것이 아니라 쉽게 쓰여있더라도 내용을 힘있게 담아내는 글이 좋은 글이라는 내용이다. 내가 읽어본 책 중 가장 인상깊었던 책을 꼽으라면 '칼 세이건'의 '코스모스'와 '스티븐 호킹'의 '시간에관한 짧은 역사'이다. 이 두 책에 담겨있는 내용은 결코 쉽지도, 가볍지도 않다. 애초에 전공자가 아니라면 이해하기 힘든 내용이 부지기수다. 하지만 두 책 모두 현학적으로 말을 꼬으거나 어렵게 쓰지 않고 쉬운 단어, 문장으로 썼다는 공통점이 있다.  그렇다보니 깊은 이해는 어렵더라도 책을 계속해서 읽어가는데 무리가 없고, 호기심도 계속 유지가되는 장점이 있다.
   마지막으로 이 책에서 꾸준히 강조하는 내용은 많이 읽고, 많이 쓰라는 것이다. 이 책을 읽는동안 계속해서 반복하는 말이다. 그러면 자연스레 좋은 글을 알게되고, 쓰기 근육이 늘어난다는 내용이다.

   이 책을 읽고나면 비단 글 쓰는 것만 도움을 받는 것이 아니다. 요즘 시대는 인터넷에 다양한 글이 존재하며, SNS를 통해 다양한 글을 접할 수 있다. 그 글들을 읽었을 때 좋은 글을 구분해 낼 수 있는 기준이 이 책을 통해서 만들어질 수 있다. 글에 관심이 있다면 꼭 한번 읽어볼 책이다.

2. Markov Chain Monte Carlo

 1. Bayesian 추론에서 MCMC를 왜 쓰는가?


   Markov Chain Monte Carlo는 각각의 시도가 이전 시도에 영향을 받지 않는(memoryless, Markov Chain) 랜덤서치(Monte Carlo)를 통하여 답을 구하는 방식이다.
   1번 포스트에서 썼던 예제는 prior belief와 posterior가 같은 형태의 분포를 갖는 conjugate prior형태이므로 닫힌 해를 이용하여 posterior를 구할 수 있었다. 하지만 대부분의 문제는 $\theta$로 표현하는 인수의 수가 많고, conjugate prior가 아닌 경우가 많아 닫힌 해를 이용하기가 힘들다. 다시 한 번 Bayes Rule의 식을 보자.
$$
P(\theta|D)=\frac{P(D|\theta)P(\theta)}{P(D)}
$$
   위의 식에서 보면 posterior의 $\theta$를 계산하기 위해서는 $P(D)$를 계산해야 하고, 이를 위해 다음의 적분을 해야 한다.
$$
P(D) = \int_{\Theta} P(D, \theta)d\theta
$$
   하지만 인수를 구성하는 $\theta$의 수가 많아질 경우 앞서 설명한대로 닫힌 해를 이용한 적분이 매우 어렵고, 그로인해 닫힌해를 가지고 posterior의 $\theta$를 추정하기가 힘들다.
 그러므로 이런 경우 posterior의 $\theta$를 구하기 위해 MCMC를 적용한다. 물론 여기서 위 적분을 직접 구하는 것은 아니다.


2. MCMC 알고리즘


   MCMC에 속하는 알고리즘은 다음과 같다. Metropolis, Metropolis-Hastings, Gibbs Sampler, Hamiltonian MCMC, No-U-Turn Sampler(NUTS), 그 외.
   이 챕터에서는 가장 오래되고 널리 쓰이는 Metropolis를 우선 설명하고, 이후 챕터에서 다른 알고리즘을 소개한다.


3. Metropolis 알고리즘


   일반적으로 MCMC 알고리즘은 다음의 순서를 따른다.

  1. 현재 인수에서 시작($\theta_{c}$)
  2. 새로운 인수로의 점프를 제안($\theta_{n}$)
  3. 이전 정보와 데이터를 이용하여 확률적으로 새로운 인수의  허용/거부를 결정
  4. 허용을 결정하면 새로운 인수로 업데이트 후 1번부터 반복
  5. 거부를 결정하면 인수를 업데이트하지 않고 1번부터 반복
  6. 지정된 횟수만큼의 시도를 한 후 허용을 결정한 인수를 모두 모음
   위의 과정에서 '어떻게 점프를 하는가'와 '어떻게 점프 여부(허용/거부)를 결정하는가'가 알고리즘마다 다르다.
   Metropolis 알고리즘은 새로운 인수를 찾아 제안할 때, 인수에 대해 가정한 분포와 상관없이 '정규 분포'를 따라 다음 인수를 제안한다. 그래서 다음 제안을 위한 $\mu$와 $\sigma$가 있는데, $\mu$는 기존의 $\theta_{c}$로 정한다. 여기서 $\sigma$를 어찌 지정하는가에 따라 수렴에 영향을 미친다. $\sigma$가 큰 값일 경우 더 넓은 영역을 살펴볼 수 있으나 허용 확률이 높은 부분을 놓치고 지나갈 수 있고, 값이 작을 경우 큰 영역을 살피는데 시간이 오래 걸리므로 수렴 시간이 더 길어질 가능성이 있다.
   이렇게 새로운 인수에 대한 제안이 만들어지면 이제 이 제안을 받아들일지 여부를 결정해야 한다. 이를 위해 가정한 $\theta$의 분포에 따라 $P(\theta_{n})$와 Likelihood $P(D|\theta)$를 계산한다. 그리고 다음의 비율을 구한다.
$$
p = \frac{ P(D|\theta_{n})P(\theta_{n}) }{ P(D|\theta_{c})P(\theta_{c}) }
$$
   그리고 $[0, 1]$에 속하는 균일 분포의 난수를 만들고, 만들어진 수가 $[0, p]$안에 들어오면 이 인수를 허용하고, 아니면 거부한다. 
   결국 Bayesian 추론에서는 새로운 인수로 만들어진 posterior $P(\theta_{n}|D)$를 시뮬레이션을 통하여 구해가는 과정인 것이다. 

   실제로 앞서 제시한 Coin toss 문제를 Metropolis를 이용하여 푼 예제를 보자. 여기서 50번의 동전 던지기에서 10번의 앞면이 나왔을 경우 posterior를 구하는 예제이다.
   위 그림에서 녹색선으로 그려진 부분이 닫힌 해를 이용한 답이고, 붉은 박스로 그려진 히스토그램이 Metropolis를 이용한 결과이다. 나름 비슷한 모양으로 답을 찾은 것을 볼 수 있다. 하지만 이 시뮬레이션의 경우 $\theta$의 수가 하나인데도, 이 결과를 얻기 위해 십만 번의 시뮬레이션을 수행해야 했다. 그러니 인수의 수가 많아질수록 더 큰 컴퓨팅 파워를 필요로 하게될 것이다.

   위 그래프는 PyMC3라는 라이브러리를 이용하여 python으로 구현한 내용이다. 코드를 다음에 첨부한다.
 
# mcmc_coin.py
# Markov Chain Monte Carlo Test for coin flipping

import matplotlib.pyplot as plt
import numpy as np
import pymc3
import scipy.stats as stats

# gg plot style의 plotting
plt.style.use("ggplot")

# parmeters
n = 50 # num of coin flip
z = 10 # num of heads
alpha = 12 # alpha for beta-distribution
beta = 12 # beta for beta-distribution
alpha_post = 22 # known updated parameter
beta_post = 52 # known updated parameter

# Metropolis algorithm에서 수행할 시뮬레이션 횟수
iterations = 100000

# PyMC3를 사용하여 model context 만들기
basic_model = pymc3.Model()
with basic_model:
    # Beta Distribution을 이용하여 prior belief 만들기
    theta = pymc3.Beta("theta", alpha=alpha, beta=beta)

    # Bernoulli likelihood 함수 지정
    y = pymc3.Binomial("y", n=n, p=theta, observed=z)

    #Metrobpolis Algorithm을 이용하여 MCMC수행
    # Maximum A Posteriori (MAP) 최적화를 initial value를 찾는데 사용
    start = pymc3.find_MAP()
    step = pymc3.Metropolis()

    # trace 계산
    trace = pymc3.sample(iterations, step, start, random_seed=1, progressbar=True)

# posterio histogram 그리기
bins = 50
plt.hist(trace["theta"], bins, histtype="step", normed=True, label="Posterior (MCMC)", color="red")

# analytic 찍기
x = np.linspace(0, 1, 100)
plt.plot(x, stats.beta.pdf(x, alpha, beta), "--", label="Prior", color="blue")
plt.plot(x, stats.beta.pdf(x, alpha_post, beta_post), label="Posterior (Analytic)", color="green")

#graph label
plt.legend(title="Parameters", loc="best")
plt.xlabel("$\\theta$, Fairness")
plt.ylabel("Density")
plt.show()


MCMC에 관해 조금 더 쉽게 설명된 내용은 http://twiecki.github.io/blog/2015/11/10/mcmc-sampling/   를 참고하기 바란다.

1. Bayesian Statistics와 Coin toss

   0. 앞으로 Michael L. Halls-Moore의 'Advanced Algorithmic Trading'을 스터디하며 요약 정리를 해 볼 심산이다. 처음으로 Bayesian Statistics와 그 예로 항상 등장하는 Coin toss 문제를 책에 제시한 내용을 가지고 요약해보겠다.


   1. Bayesian vs. Frequentist


   통계 문제에 확률을 적용하는 방법은 크게 'Bayesian'과 'Frequentist'가 있다.  'Frequentist'는 확률이란 어느 사건이 아주 많은 수의 수행 속에 나타난 빈도라고 생각하는 것이다. 그래서 이 경우 ideal 확률을 알아야 하며, 이 값이 변하지 않는다.
 'Bayesian'의 경우, 이미 일어난 사건(data)와 앞으로 일어날 사건을 바탕으로 우리가 믿고있는 확률을 변화시킬 수 있다고 한다.
 최근 이슈가되는 Machine Learning이나, 예전부터 많이 사용되는 Time Series Analysis는 모두 Bayesian Statistics에 바탕을두고있다.


   2. Bayes' Rule

 
   Bayes' Rule은 조건부 확률(conditional probability), 즉, 'B라는 일이 일어났을 때 A라는 일이 일어날 확률은 얼마인가?'에 대한 답을 찾기 위함이다. 이는 다음의 식으로 나타낼 수 있다.
$$
P(A|B) = \frac{P(A\cap B)}{P(B)} \\
P(B)P(A|B) = P(A\cap B)
$$
  그리고 'A가 일어났을 때 B가 일어날 확률' 또한 다음과 같이 표현할 수 있다.
$$
P(B|A) = \frac{P(B\cap A)}{P(A)} \\
P(A)P(B|A) = P(B\cap A)
$$
 여기서 $P(A\cap B) = P(B \cap A)$이므로 이를 풀어 쓰면 다음과 같다.
$$
P(A|B) = \frac{P(B|A)P(A)}{P(B)}
$$
 $P(B)$는 다음과 같이 표현할 수 있다.
$$
P(B) = \sum_{a \in A} P(B \cap A) = \sum_{a \in A} P(B|A)P(A)
$$
 즉, 교집합에 대한 확률을 모든 A의 이벤트에 대해 더해준 값이다. 그러므로 마지막으로 조건부 확률을 다음과 같이 쓸 수 있다.
$$
P(A|B) = \frac{P(B|A)P(A)}{ \sum_{a \in A} P(B|A)P(A)}
$$


   3. Coin flipping에 Bayes 추론 적용하기

 
   우리가 동전 던지기에서 알고싶은 것은 '동전이 얼마나 공정(fair)한가?'이다. 즉, 동전의 앞면이 나올 확률이 정말 거의 0.5에 가까운가를 알고싶은 것이다. Bayes 추론을 이 문제에 적용하기위해 다음과 같은 과정을 따라야 한다.

 1) Assumptions - 동전은 무조건 두 결과(앞, 뒤) 중 하나만 나온다고 가정한다. 물론 동선을 던졌을 때 서 있을 수 있지만, 이런 상황은 제외한다. 그리고 각 시도에서 나오는 결과는 다른 시도에서 나오는 결과와 독립적이며, 이 공정성(fairness)는 어느 시도에서건 안정적(stationary)이므로 시간에 따라 달라지거나 하지 않는다. 여기서 공정성은 $\theta$라는 인수로 표현하자.
 2) Prior Beliefs - 추론을 적용하기 위해 이 prior beliefs(선행 믿음?)을 정량화해야 한다. 이는 우리가 찾아볼 공정성의 분포를 특정하는 것으로 귀결된다.
 3) Experimental Data - 이제 동전을 던져 실제 데이터를 뽑아낸다. 이 때 어떤 $\theta$가 주어졌을 때 해당 실제 결과가 나올 확률이 필요한데, 이 것이 likelihood function이다.
 4) Posterior Beliefs - 이제 앞의 Bayes' rule을 이용하여 데이터에 기반한 새로운 확률을 구한다. 앞서 prior beliefs 과정에서 Beta distribution와 Bernoulli likelihood function을 사용하면 posterior에서도 beta distribution을 얻을 수 있는데, 이렇게 posterior와 prior가 같은 형태의 분포로 표현되는 것을 conjugate priors라고 한다.
 5) Inference - 구해진 posterior belief를 기반으로 동전의 공정성을 추정한다.

 앞서의 Bayes' Rule로 표현한 다음 식과 위의 과정을 대입해보면 다음과 같다.
$$
P(\theta | D) = P(D|\theta)P(\theta) / P(D)
$$
 여기서 D는 data이다.

  • $P(\theta)$ - prior. 여기서 data D에 대한 감안은 없다.
  • $P(\theta | D)$ - posterior. 수정된 우리의 믿음이라고 보면 된다.
  • $P(D| \theta)$ - likelihood. 어떤 인수 $\theta$가 있을 때 관찰한 데이터가 나올 확률이다.
  • $P(D)$ - evidence. 모든 $\theta$에 대해 D가 나올 확률을 더한 확률

 3.1 Bernoulli Distribution의 Likelihood Function


 
확률 변수 $k$가 동전 던지기의 결과를 나타낸다고 하면 $k \in \{ 0, 1 \}$이다. 여기서 앞면이 나올 확률(공정성)을 $\theta$라 했으니, $\theta$가 주어졌을 때 $k$의 확률을 다음과 같이 쓸 수 있다.
$$
P(k|\theta) = \theta^{k}(1-\theta)^{1-k}
$$
 위의 식을 다시 해석하면 특정 $\theta$가 주어졌을 때 $k$의 확률을 나타내므로 결국 likelihood function으로 해석할 수 있다.
 이제 동전을 여러번 던진다고 하자. 각각의 던지는 행동은 독립적이므로 각각의 확률을 곱하면 총 확률을 구할 수 있다. 즉, 다음과 같은 식으로 쓸 수 있다.
$$
P(\{k_1, \ldots , K_N\}|\theta) = \prod_{i} P(k_i | \theta) = \prod_{i} \theta^{k_i}(1-\theta)^{1-k_i}
$$
 N번 던졌을 때 z번의 앞면이 나왔다면 위의 식은 다음과 같이 쓸 수 있다.
$$
P(z, N|\theta)=\theta^{z} (1-\theta)^{N-z}
$$

 3.2 Prior Beliefs의 정량화


   앞서 얘기했듯이 $\theta$에 대한 분포를 일단 정해야 한다. 여기서 $\theta \in [0, 1]$이니 beta distribution을 사용한다. beta distribution의 PDF(확률밀도함수)는 다음과 같다.
$$
P(\theta | \alpha, \beta) = \theta^{\alpha - 1} ( 1- \theta)^{\beta - 1} / B(\alpha, \beta)
$$
 여기서 $B(\alpha, \beta)$는 값을 0과 1사이에 위치시키기 위한 표준화 상수(normalization constant)로서 다음과 같이 쓴다.
$$
B(\alpha, \beta) = \frac{\Gamma (\alpha)\Gamma (\beta)}{\Gamma (\alpha + \beta)} = \frac{(\alpha - 1)!(\beta - 1)!}{(\alpha + \beta - 1)!}
$$
 위 분포의 $\alpha$와 $\beta$에 따른 모습은 다음 그림과 같다.

   Beta prior(beta distribution으로 표현한 prior)의 $\alpha , \beta$를 이용하여 조금 더 친숙한 mean, variance를 표현할 수 있다. 이는 다음과 같다.
$$
\mu = \frac{\alpha}{\alpha + \beta}, \\
\sigma = \sqrt{\frac{\alpha \beta}{(\alpha + \beta)^2(\alpha + \beta + 1)}}
$$

 3.3 Posterior 계산하기


 Bayes' rule을 이용하여 posterior를 바꿔보자. 일단 위의 likelihood 함수 등을 이용하면 다음과 같이 표현할 수 있다.
$$
\begin{eqnarray}
P(\theta | z, N) &=& P(z, N|\theta)P(\theta) / P(z, N) \\
  &=& \theta^{z} (1-\theta)^{N-z} \theta^{\alpha - 1}(1-\theta)^{\beta - 1} / [B(\alpha, \beta)P(z, N)] \\
&=& \theta^{z+\alpha - 1}(1-\theta)^{N-z+\beta - 1} / B(z+\alpha, N-z+\beta)
\end{eqnarray}
$$
 실제 coding을 해서 그 distribution을 그려보면 다음과 같이 횟수가 늘어날 수록 $\theta$가 0.5 근처로 몰리는 것을 볼 수 있다.

북유럽 신화(닐 게이먼, 2017)




   0. 마블 코믹스의 영화가 세계적으로 히트를하면서 '어벤저스' 등에 나온 '토르', '로키'는 이제 매우 유명한 캐릭터가 되었다. 그리고 판타지 소설의 고전이라 불리는 '반지의 제왕'이 있고, 최근에는 '왕좌의 게임'이 있다. 이 두 판타지 소설들은 그 설정이 많은 부분 북유럽 신화에 나오는 내용에 바탕을 두고 있고, '토르'와 '로키'는 아예 북유럽 신화의 주인공들이다.

   1. 시초의 거인 이미르에서 시작하여 오딘, 그의 아들 토르와 거인족 라우페이의 아들 로키가 겪는 일들이 북유럽 신화 이야기의 큰 축을 이룬다. 그리고 마지막장의 라그나로크에서 신들의 마지막 싸움과 멸망을 그린다.
   북유럽신화는 그리스 신화와 다르게 상당히 내용이 거칠고 어둡다. 저술 과정에서 아마도 순화되었겠으나, 꽤나 잔혹한 장면이 많고, 별 것아닌 동기로 잔혹하게 상대방을 살해하는 듯한 내용도 꽤 많다. 아마도 이것은 이야기가 전승되어오는 북유럽의 고대 바이킹들의 성정이 반영된 것이 아닌가 한다. 전승되어오는 국가 또는 장소의 역사와 신화의 분위기를 엮어본다면 꽤 재미있는 주제가 될 듯하다. 실제 그리스 신화의 신들은 매우 부유하고 사치스럽다.

   2. 닐게이먼은 알고보니 꽤나 유명한 작가였다. 그래픽노블 작가이고, 그 외 소설로 상도 많이 탔다. 덕분에 책의 내용들이 술술 읽히며 이야기가 매우 흥미롭다. 번역도 나름 괜찮은 편이어서 읽는데 부데끼지 않는다. 기분 전환하기 참 좋은 책인 듯 싶다. 

잡설 3 - 오늘의 운세

   2003년이었던 것 같다. 대학원 진학 후 좋지 않은 일들이 있었고, 대인관계가 힘들어 하루하루가 고통이었다. 답도 잘 안나오고 하니 인터넷으로 운세도 찾아보고 그랬다. 그러다 전화로 하는 유료 운세상담을 5천원 주고 해보기도 했었다. 그 때 말로는 6월인가 지나면 나아질 것이라고 했는데, 사실 별 일이 일어나지도 않았고 그냥 그런 날들이 계속되고 있었다.
    2014년도 비슷했다. 처음 트레이딩이라는 것을 시작하고 잘못된 모형으로 손실을 입었을 때, 여기저기서 들어오는 압박이 너무 힘들기도 했고, 처음 겪는 일에 괴로움의 강도가 너무 강했었다. 그 때도 여기저기 오늘의 운세나 보며 무언가 돌파구가 생기길 바랬었다. 하지만 역시나 맞는 것은 별로 없었고, 그저 시간이 지나면서 어떻게 어떻게 흘러갔다.
   그런 일들을 겪으며 그나마 배운 것은 그래도 시간이 지나면, 물론 허투루 시간을 보내지 않는다는 기본 가정을 지킨다면, 일이 어떻게든 해결은 난다는 것이었다. 그 것이 유야무야 되어 잊혀지든, 아니면 해결 방안이 생기든 말이다.

   요즘 내 모습을 보면 저 때와 비교해 그다지 나아진 것 같지는 않다. 시간이 지나더라도 어떤 일들이 고통스러운 것은 그대로이고, 그 때마다 마음이 급해져 요행을 바라는 것도 말이다. 요즘도 혹시나 하는 기대로 오늘의 운세를 뒤적거리기도 한다. 지금처럼 삶을 잘 유지하며 버티면 다 끝이 보이겠지만, 그럼에도 마음이 급해지는 이유는 무엇일까?

옥자(2017, 봉준호)




   0. 무려 500억 가량의 제작비를 들여 만든 넷플릭스 오리지널 무비다. 스토리 설명이야 워낙 많은 곳에서 하고있으니, 간단히 소감만 적어보려한다.

   1. 옥자에서 좋았던 점은, 일단 그래픽과 옥자 캐릭터가 좋다. 서글서글한 눈매에 하마인 듯하고, 다시 보면 매너티 같기도 한 이 슈퍼돼지는, 표정만 봐도 편안하고 기분이 좋아진다. 거기에 안서현양이 분한 미자와 함께 꽁냥거리는 장면은 흐뭇하게 바라볼 수 있다.
   그리고 이 영화의 메시지가 좋다. 개인적으로 채식주의자도 아니고 가히 고기테리안에 가까운 나지만, 한 번쯤 대량생산 품목처럼 취급되는 동물들에 대해 생각해볼만한 기회를 준다. 결론 자체도 의외인데, 한 마디로 설명하자면, 판타지같은 영화에 너무나도 현실적인 결말이다. 물론 이 결말때문에 호불호가 갈리는 듯한 인상이 있다.
   마지막으로 배우들의 연기가 좋다. 어느 하나 발연기가 없어 마음에 들었다.

   2. 그런데, 이 영화가 몇가지 애매한 점이 있다. 일단 캐릭터들이 색이 조금 불 분명하다. 전작 '괴물', '마더' 등에서는 캐릭터가 꽤나 색이 강하고, 그럼에도 영화속에서 각과 부드러움을 적절히 버무렸는데, '옥자'에선 그런 것들이 조금 약했다. 그리고 결말이 어떤 영화적 카타르시스를 기대하기는 무리이다보니, 호불호가 갈릴 수밖에 없다. 하지만 영화 자체의 내용을 놓고보면 결말로 가는 과정이 어거지거나, 난삽하거나하진 않아 보인다.  개인적으로 결말로만 보면 '마더'가 압권이었다. 그 뽕 맞은 듯한 엔딩은...

   3. 봉감독의 헐리웃 데뷔작인 '설국열차'는 처음엔 뭔가 무거웠지만, 그래도 두 세번 보더라도 재미가 반감되는 느낌은 없었다. 그래서 국내에선 호불호가 갈렸지만, '설국열차'를 꽤 좋아한다. 이번 '옥자'는 어떨지 모르겠다. '설국열차'에 비하면 힘이 많이 빠졌지만, 메시지는 더욱 직접적이다. 캐릭터는 조금 약해도 헐리웃 배우들의 명연기를 보는 맛은 있다. 결론적으로, 나는 재미있게 잘 봤다.

Uncopyable class와 clone 함수

   C++의 경우 class를 생성하면 기본적으로 default constructor와 copy constructor, 대입 연산자가 기본으로 만들어진다. 이 세가지는 프로그래머가 작성하지 않더라도 컴파일러가 알아서 마치 사람이라면 밥 먹고 화장실 가듯이 자연스럽게 만들어 버린다.

   C++의 가장 어려운 점이자, OOP(Object Oriented Programming)이라면 어느 언어든 가지고 있는 문제는 객채의 state 관리이다. Class도 하나의 type으로 보므로 각종 사칙 연산의 overloading이 가능한데다, 구조 안에 다양한 class의 객체를 state variable로 가질 수 있어 까딱 잘못하단 지옥문이 열린다. 특히 멀티 쓰레드 환경에선 더 심하다.(덕분에 Functional Programming 추종자들에게 대차게 까이지만, FP도 VM은 C++로 만든게 많다. -_-)

   이런 고통을 조금이나마 완화할 수 있는 방법은, class 자체의 복사를 막아버리는 것이다.  물론 다 막으라는 것이 아니라, 한 클래스의 각각의 객체가 서로 독립적인 상태를 가져야 한다든가, copy는 허용하고 싶은데 내용물에 pointer가 있다던가 하는 경우에 고려할만하다. 특히 후자의 pointer를 내용물로 가졌을 때는 앞에 소개할 방법을 심각하게 고려해봐야 한다.

   다음과 같은 코드를 실행해보자.


class B 
{
public:
    B(int i)
    {
        k = new int;
        (*k) = i;
    }

    int val()
    {
        return *k;
    }

    int* valp()
    {
        return k;
    }
private:
    int *k;

};

...
B b(1);
B b3 = b;

std::cout  << "b : "  << b.valp()  << std::endl;
std::cout  << "b3 : " << b3.valp()   << std::endl; 

   이 코드를 실행하면 다음과 같은 결과를 얻을 수 있다.


 b : 0047AD50
b3 : 0047AD50

   즉, 같은 위치의 메모리 영역을 가리키게 되고, 한 객체에서 메모리 영역 내의 값을 바꾸면 다른 객채까지 영향을 준다.


   이런 문제를 해결하기 위해서는 일단 class의 기본적인 복사를 막는다. 다음과 같이 Uncopyable이라는 클래스를 선언하고 copy constructor와 대입연산자를 private으로 선언한다.


class Uncopyable
{
public:
    Uncopyable() {}
    ~Uncopyable() {}

private:
    Uncopyable(const Uncopyable&);
    Uncopyable& operator=(const Uncopyable&);
};



    그리고 다음 처럼 위의 클래스를 상속한 클래스를 만들어주자.
 class A : private Uncopyable
{
public:
    A(int i)
    {
        k = i;
    }

    int val() 
    {
        return k;
    }
private:
    int k;

};

A a(1);
A a2 = a; 


    위의 코드를 컴파일하면 Uncopyable 클래스에 대한 access violation이 일어나면서 컴파일이 되지 않는다. 그리고 베이스 클래스에 가상 함수로 clone이라는 것을 지정하여 하위 클래스가 clone을 구현하도록 하면 내가 원하는 방식으로 객체 복사를 할 수 있다.
 
class Base 
{
public:
   ...
   virtual Base* clone() = 0;
}


   개인적으로도 이 방식을 잘 활용하는데, 아직까지는 꽤 성공적인 듯 하다.

잡설 2 - 고개 숙임

   90년대 월드와이드웹이 개발되어 퍼지고, 네트워크를 컴퓨터에 연결하면서 사람들은 새로운 세상을 경험했다. 어디서든 컴퓨터가 있는 곳이라면 누구나 정보를 열람할 수 있고, 각종 채팅 서비스들로 일면식도 없는 사람과 대화를 나누었다. 그리고 그와 함께 사람들이 모이는 커뮤니티라는 것이 생기고, 그 안에서 각자의 모습을 분출하며, 누군가는 그 안에서 스타가 되기도 하였다.

   2008년 경 아이폰의 출시는 이런 인터넷 사용 모습을 완전히 바꾸어놓았다. 물론 아이폰 이전에도 인터넷이 되는 전화기는 이미 많이 있었고, 스마트폰이라는 개념도 사실 없던 것이 아니었다. 하지만 누구나 앱(app)을 개발할 수 있고, 다양한 기능을 앱을 통해 사용할 수 있다는 개념을 대중화시킨 것은 분명 아이폰이다.

   이렇게 손으로 들고다닐 수 있는 전화기는 통신망을 통해 언제 어디서나 인터넷에 접속할 수 있었고,  이를 바탕으로 다양한 서비스가 폭발적으로 성장했다. 그러다보니 사람들은 언제 어디서나 다른 사람들의 소식을 들을 수 있었고, 자신의 소식과 생각을 공유할 수 있었다.

   출퇴근 시간에 전철이나 버스에서 고성능 스마트폰으로 여러 소식에 심취해있는 모습은 이미 오래된 풍경이다. 그리고 길을 가다 무슨 일이 생기면 그 것을 공유하기 위해 스마트폰을 들이대는 모습도  현 시대 인류의 스테레오타입이 돼버린지 오래다.  물론 나도 예외는 아니리라.
 
   이런 고개 숙인 인간상은 비단 교통수단 안에서 혼자 있을 때만 한정되진 않는 것 같다. 식당이나 공원, 또는 여러 곳에서 여러 사람이 모여있더라도 서로 이야기를 나누기 보단 고개를 숙이고 스마트폰을 본다. 분명 만나고 싶어서 만난 사람들일텐데, 옆에 있는 사람보다 더 멀리있는 누군가의 소식이 더 궁금한가보다.
   그리고 길을 가다보면 무엇이 그리 궁금한지 스마트폰에 눈을 고정하고 위태위태하게 걷는 사람들도 많이 있다. 물론 그들의 사정이 있겠지만, 길을 가는 사람들을 너무 방해하는 것이 아닌가 하는 생각도 든다.

   어디서나 시간과 공간에 구애받지 않고 소식을 얻을 수 있는 자유는 얻었다. 하지만 그 것이 과연 진정한 연결을 제공하는지는 다시 생각해볼 문제인 것 같다.

인생논어 - 1

  0. 조형권님이 쓴 <<인생논어>> 를 읽고 필사한다는 생각으로 구문들을 옮겨 적으려 한다.  1. 나만의 속도를 유지하라.   子曰, 射不主皮 爲力不同科 古之道也 (자왈, 사부주피 위력부동과 고지도야)  해석: 활을 쏠 때 ...