2017년 12월 13일 수요일

서평 [IT 트렌드 스페셜 리포트]를 읽고

IT 트렌드 스페셜 리포트

이 책은7가지의 IT 기술을 개괄적으로 설명을 하고 있다.

어찌보면, 누구나 들어봤을 이야기들이다. 이 책이 아니더라도 어디서든 IT트렌드들은 읽을 수 있을 것이다.
하지만 이 책이 다른 점은 IT트렌드가 무엇인지를 알려주는 것이 아니다.
실제 우리가 들어본 IT기술들이 어떻게 쓰이고 있는지 알려준다. 단순히 이곳에서 쓰이고 저곳에서 쓰인다로 끝나는 것이 아니라.
어떻게 설계되어 있는지 무엇을 확인해야 하는지 그런 실질적인 내용도 담고 있다.

또 눈여겨 보아야 할 점은 현업에 종사하는 사람의 이야기와 그들이 실제로 어떻세 사용하고 있는지 적어노았다.
단순히 근무환경이나 비전을 묻는 것이 아닌, 실제 기술을 묻는다.

이 책은 단순히 넓은 식견을 넓혀주는 것이 아니라.
놓치기 쉬운 부분들. 어쩌면 IT트렌드를 쫓아가야 할 때, 알아야할 세세한 부분을 설명한다.
물론, 책의 내용은 적지만 어디를 보고 나아가야 할지 짚어준다.

하지만 짧은 페이지 수는 좀 아쉬울 수 있다. 좀 더 많은 사례들이 나왔으면 싶었다.
각 장당 한 회사의 사례만으로는 성이 차지 않는다.

그럼에도 읽을 만한 가치가 있다고 생각한다.

2017년 12월 3일 일요일

[clojure][designpattern] 클로저 디자인패턴 - 전략패턴

clojure design pattern 출처::http://clojure.or.kr/docs/clojure-and-gof-design-patterns.html
2. 전략패턴(Strategy Pattern)
요약 : 이름 정렬 , 유료고객은 먼저.

(def users [{:subscription true :name "Y"} {:subscription true :name "Z"} {:subscription false :name "Z"}])

(sort (comparator (fn [u1 u2]
                    (cond
                      (= (:subscription u1) (:subscription u2))
                      (neg? (compare (:name u1) (:name u2)))

                      (:subscription u1)
                      true

                      :else
                      false)))
      users)
위 소스는 comparator를 만들고 하는 것이 아주 비슷하다.
아래 소스를 봐보자.
(sort-by (juxt (complement :subscription) :name) users) ;; forward sort
(sort-by (juxt :subscription :name) #(compare %2 %1) users) ;; reverse sort
워워 하나씩 보자.
(sort-by count ["aaa" "bb" "c"])

("c" "bb" "aaa")
juxt 이녀석은 뭘까? (juxtaposition : 병렬, 병치)
((juxt a b c) x) == [(a x) (b x) (c x)]
(for [user users]
  ((juxt :subscription :name) user))
;; ([true "Y"] [true "Z"] [false "Z"])


(for [user users]
  ((juxt (complement :subscription) :name) user))
;; ([false "Y"] [false "Z"] [true "Z"])

complement (보수 말하는건가) : 함수를 받아서 true/false의 반대값을 내보냄
(def odd?? (complement even?))
(odd?? 3)
true 
이걸 먼저 알아보자
(sort-by (juxt (complement :subscription) :name) users) ;; forward sort
user=> ((complement :subscription ) {:subscription "HI"})
false
user=> ((complement :a ) {:b "HI"})
true

두 줄로 만들어지는 것이 신기하다. juxt는 엄청난 함수 인 것 같다. 나중에 쓸일이 많을 것 같다.

2017년 11월 30일 목요일

[clojure][design pattern] 클로저 - 커맨드패턴

clojure design pattern 출처::http://clojure.or.kr/docs/clojure-and-gof-design-patterns.html#command


커맨드패턴(Command Pattern)

보면 알겠지만 커맨드(명령)을 login/logout명령으로 나눠서 따로 구현했다.
구현할 때 Command 인터페이스를 구현하여 execute(실행해라!)모든 명령을 이 인터페이스가 있는 녀석으로 구현하면
뭐든 간에 execute만 실행하면 될 것이다.

(defn login [id pass] (println "id: " id ", pass: " pass))

(defn logout [id] (println "logout: " id))

(defn execute [command]
  (do
    (println "logging")
    (command)))

;; 익명인터페이스와 동급

(execute #(login "name" "123"))  

(execute #(logout "idid"))

(defn execute [command & args]
  (apply command args))

(execute login "id" "password") 
apply를 사용하는 이유는 [command & args]에서 args는 리스트형태(?)로 반환하기 때문에 이 리스트 괄호를 깨부시고 안에 들어가서 적용(apply)해야 한다.

(defn wrong-execute [command & args]
  (command args))
(wrong-execute login "id" "password")

ArityException Wrong number of args (1) passed to: user/login  clojure.lang.AFn.throwArity (AFn.java:429)
보는바와 같이 login에는 잘못된 숫자의 매개변수가 들어간 것이다.
아마
(login ["id" "password"])
요래 들어갔을 것이다. 이렇게 실행해보자.
user=> (login ["id" "password"])
ArityException Wrong number of args (1) passed to: user/login  clojure.lang.AFn.throwArity (AFn.java:429)
똑같다 이제 apply를 해보자
user=> (apply login ["id" "password"])
id:  id , pass:  password
nil
아까 apply를 이용하면 괄호들을 깨부수고 안으로 들어(?)간다고 했다.
(apply login ["id" "password"]) == (login "id" "password")
요래 들어가는 것!!! (apply를 꼭 써야 한다.)
============================

이게 왜 중요하다는 거지?

일단 커맨트패턴이 뭔지보자. (자바의 눈으로)

execute()하나를 함께 쓰기 위해서 Command라는 인터페이스를 각각 구현하는 것이다.
그러면 login/logout을 하기 위해 login,logout클래스를 만들고 Command라는 인터페이스를 구현할 것이다.

자 그렇다면 로그인을 위한 Command 인터페이스를 클래스로 구현했다. 그리고 이제 이 클래스를 사용할 것이다.
아니!
정확히는 이 클래스에 있는 Command 안에 execute을 사용할 것이다.

이 커맨드들을 어딘가에 뒀다가 가져오려면 리턴도 되야하고 매개변수로 넣어지기도 해야 할것이다.
클래스는 그럴 수 없다.(뭔줄알고!!) 메소드를 넣을 수도 없다. 인터페이스를 넣는 것이다.
execute를 사용하기 위해!!

클로저의 눈으로 보자.
일단 클로저에서는 함수를 그냥 넘기기 때문에 개념이 아예 다르다.
그냥... 뭔 만들고 넣고 하는게 아니라...
함수를 보내고 받은걸 쓰면 된다.

뭐가 더 좋다가 아니라 이 두 개념을 마음대로 노닐 수 있는 내가 되었으면 좋겠다.

2017년 11월 16일 목요일

[clojure] loop: doseq,dotimes, while, loop, recur 정리

출처 : Clojure Loop

doseq

foreach 문과 비슷하다. 각각의 요소(element)를 돈다(iterate). 중요한 것은 lazy sequence를 강제(force) 평가(evaluation)한다.

(doseq [n (range 10)]
  (println n))
0
1
~
9

dotimes

일반 for문과 비슷하다. 숫자만큼 포문을 돈다.

(dotimes [i 10]
  (println i))
0
1
~
9

while

단순 while문과 비슷

(def a (atom 0))
(while (< @a 10)
  (do
    (println @a)
    (swap! a inc)))
0
1
~
9

loop/recur

loop를 이용하여 재귀를 돌 범위를 정한다. recur을 이용하여 loop의 값을 바꾼다.

(loop [i 0]
  (if (< i 10)
    (do (println i) 
        (recur (inc i)))
    i))
0
1
~
9

defn/recur

함수를 재귀

함수 이름을 부르지 않고 recur을 사용하는 이유는 tail recursion때문이다.

(defn increase [i]
  (if (< i 10)
    (do (println i)
        (recur (inc i)))
    i))
(increase 0)
0
1
~
9

[java]이클립스 JDK 패스 설정

-vm
 C:\Program Files\Java\jdk1.8.0_152\bin\javaw.exe
이런식의 내용을 eclipse.ini 에 맨 위에 넣는다.

2017년 8월 27일 일요일

Udacity Robotics Nanodegree Program 첫번째 프로젝트를 하면서 느끼는 점.

Rover Control

유다시티 강의의 첫번째 프로젝트. 로버가 저절로 길을 찾아가기이다.
Robotics 강의가 뭔지 잘 몰랐고, C++가 필요하다 하길래 C++와 나름 친숙해지고 자신감을 가지고 수강을 했다.

낭패였다

Robotics에서는 로봇을 칭할 때, 3가지가 필요하다.
  1. Perception
  2. Decision Making
  3. Action
C++ 같은 것이 중요한 것이 아니라.
  1. 상황을 인지하고
  2. 어떻게 할지 결정하고
  3. 행동을 하는 것
이것을 어떻게 해내야 할지. 고민하고 기법들을 좀 더 공부해 놓는 것이 더 도움이 될 것이었다.
파이썬을 회사에서 사용해보긴 했지만, 데이터 시각화나 수학적 계산은 다른 문제다.
여차여차 유다시티에서 하는 강의들을 따라가고 있지만 과연 얼마나 더 할 수 있을지는 미지수다.
주말마다 이것 때문에 어딜 나가지 못한다...
정말 이번에 느낀 것은... 선형대수가 정말 중요하다는 점!
계속 공부하지 않으면 안되겠다!

게다가 나는 자바로 개발을 하고 있는데도 요즘 자바에 너무 관심을 끈게 아닌가 싶다.
자바에도 따로 관심이 있는 분야가 있는데(JAVA - ASM 라이브러리 이다. 정말 멋진 기술이라고 생각한다.)
언젠가 이것도 좀 더 공부해서 토이프로젝트로 하나 뭔가 만들어봤으면 좋겠다. (ASM하나만을 이용해서!)
너무 주저리 주저리 쓴건가.
아래에 현재 하고 있는 프로젝트의 링크를 담아놓아야겠다.

2017년 8월 17일 목요일

[서평] Node를 사용한다면 어디로 가야 하는지 가르쳐주는 [Node.js 6.x 블루프린트]


나는 Node.js에 대해 잘 알지 못한다. Node.js 자바스크립트만 알면 된다는 장점을 내새워 치고 올라오고 있는 기술 중 하나이다.
Node.js는 커지는 신생 기술인만큼 변화의 물결이 크다.(지금도 초기 만큼 변화가 큰지는 모르겠지만) 그만큼 개발자체와 새로운 변화를 사랑하는 사람들에게는 행복할 것이다.

해도해도 너무하다는 말도 많을 것이다.

내가 Node.js에 대해 잘 알지도 못하면서 이런 말을 하는 이유는 Node관련 세미나를 한 번 견학한 적이 있기 때문인데 그때도 1년에 다르게 새로운 기술이 나오고, 이전 기술이 옛것이 되면서 크게 발전하고 있다는 것이다.

그러므로 기존에 사용하던 모듈이 금방금방 deprecated 되는 현상을 맞볼 것이라고 한다.

예를 들자면,
이 책에서 promise라는 기술을 콜백 지옥에서 벗어나기 위한 기술로 소개한다. 하지만 내가 견학했던 세미나에선 이렇게 말했다.

"Promise도 이젠 옛말이죠. 이런 다른 걸 씁니다."

이 격한 파도의 물결을 온몸으로 받아치고 싶다면, Node.js를 그래도 알아야만 한다면

당신은 이 책을 읽어야 한다.

이 책은 말그대로 청사진이다. Node.js는 너무 많은 변화가 자칫 어디로 가야할지 헷갈리게 할 수 있다. 여기저기서 소위 약파는(?) 기술들이 많기 때문이다.

이 책을 한 번 읽으면 Node.js라는 기술이 어디로 가고 있는지 알 수 있을 것이다.

2017년 8월 8일 화요일

Udacity Robotics Nanodegree Program 수강시작!

인강 하나 듣는데 전공, 경력, 게다가 자소서까지 썼다. 이렇게까지 해서 들을 가치가 있을까. 했지만 쓰는게 어려운 것은 아니므로 보냈다. 

Robotics Nanodegree slack을 바로 열어줬는데, 다들 이미 로봇전공인 사람들이 많아보여서 나는 아무말도 하지 않고 가만히 있었다. (분명 떨어졌으리라 생각했다.)

그리고 2개월이 지나서 유다시티 Robotics Nanodegree를 수강 할 수 있게 되었다.

보기에는 엄청난(?) 경쟁률을 뚫고 내가 되었다는데 사실여부를 알 수 없다.

가격도 엄청나게 비싸다. 게다가 저게 끝이 아니다. (한 번 더 내야 하는...)

Cousera, Udemy, Udacity를 전부 사용해보았지만 항상 느낀 것이 Udacity에는 활기가 넘치는 것이 느껴진다. 가르치는 것을 즐기는 듯한 느낌이 들어서 제대로 돈을 주고 배워보고 싶은 마음이 있었다.

하지만 내 일과가 Udacity가 전부는 아니기 때문에
과연 잘 할 수 있을지 의문이지만, 
나는 열심히 해왔고, 또 더 열심히 할 것이니까
해낼 것이다.

2017년 7월 17일 월요일

[python][django] RuntimeWarning: DateTimeField received a naive datetime 에러

DateTimeField에 날짜를 테스트하려 할 때 문제가 발생했다.
from datetime import datetime
AA.objects.create(my_date=datetime.now())
장고는 날짜에 Timezone을 추가해서 사용한다. timezone을 함께 넣어주던가
사용하지 않는다고 명시하던가 해야한다.
from django.utils import timezone
AA.objects.create(my_date=timezone.now())

[python][django] Model 테이블의 이름을 변경하려면 어떻게 해야 할까?

makemigrations 손보기

ORM에 대해서 많이 들어보고 그냥 쓰면 좋다는 생각에..
그리고 장고를 사용하면 자연스럽게 쓸 수밖에 없기에 그 편리함에 놀라워했다

makemigrations와 migrate만 알면 전부 다 될 줄 알았다. 하지만 이제 알겠다.
편리하기 위해선 많이 배워야 함을... 어쩌면 한 번도 사용한 적이 없어서일지도 모르겠다.
여하튼
단순히 Model명을 변경하고 makemigrations를 하면 에러가 난다.
장고는 내 맘과 같지 않아서, 이 모델이 rename된건지 확신하지 못하는 것같다.
그러면 내가 확신을 가지도록 명시해줘야 한다.
일단 원래대로 돌려놓고
python manage.py makemigrations --empty <앱이름>
이렇게 하면 makemigrations의 빈 통이 보인다. 다른건 보지말고 얘만 보자.
...
operations = [
]
이 안에다가 무슨일을 한 건지 적어준다.
operations = [
  migrations.RenameModel(
    old_name='Pattern',
    new_name='MessagePattern',
    ),
]
이후 Model명을 바꾸고 사용하고 있는 다른 파일들 모델명을 전부 바꾼다.

출처: 이것저것 바꾸다가 makemigrations 스키마가 이런걸 사용하는 것을 확인. python 도큐먼트를 뒤늦게 확인

[python] 파이썬 디렉터리 삭제하기

os.removedirs, shutil.rmtree

단순히 디렉터리를 삭제할 때는 os.removedirs를 사용하는 것으로 가능하다.
import os

os.removedirs(some_path)
하지만 디렉터리 안에 파일이 있다면 삭제되지 않는다. 그럴땐
import shutil
shutil.rmtree(some_path)
이렇게 삭제하자

2017년 7월 7일 금요일

[python] eval 악마와의 계약

eval()을 사용해보자

후위표현식 AB+를 A+B로 변환해서 산술연산하는 함수를 만들자.
def postfix_eval(expr):
    operand_stack = []
    token_list = postfix_expr.split()

    for token in token_list:
        if token in '0123456789':
            operand_stack.append(int(token))
        else:
            operand2 = operand_stack.pop()
            operand1 = operand_stack.pop()
            result = do_math(token, operand1, operand2)
            operand_stack.append(result)
    return operand_stack.pop()

def do_math(op, op1, op2):
    if op == "*":
        return op1 * op2
    elif op == "/":
        return op1 / op2
    elif op == '+':
        return op1 + op2
    else:
        return op1 - op2
실행
print(postfix_eval('7 8 + 3 2 + /'))
여기서 주목할 것은 do_math
여기 do_math의 내용을 이렇게 바꾼다면
def post_math(op, op1, op2):
    return eval(op1 + op + op2)
테스트를 해보자.
post_math('+', '1', '2')
3
문자열을 그대로 맘대로 합쳐서 그 문자열 자체를 평가
이런 것에 대한 엄청난 효과를 볼 수 있는 것은 리스프 형태일 것 같다.
다음엔 python 리스트 형태 언어인 hy로도 조금씩 적어봐야겠다.

2017년 7월 4일 화요일

Extra Long Factorials python3

사실 푼 것도 아님. 그런데 너무 빨라서... 널 쓸 수 밖에 없었단다...
#!/bin/python3
import sys
from math import factorial

n = int(input().strip())

print(factorial(n))

2017년 7월 1일 토요일

[python][트리] 백준 알고리즘 1991번

테스트를 위해 인풋을 파일로 만들었음.
할일
  1. 파일을 읽어온다.
  2. 파일을 정재하여 리스트에 넣는다.
  3. 간단한 트리구조로 변환할 함수를 만든다
  4. 트리의 루트를 글로벌로 하나 생성한다.
  5. 트리구조의 리스트로 변환한다
  6. 전위, 후위, 중위 탐색을 함수를 만든다.
  7. 출력한다.
1. 파일을 읽어온다. 2. 파일을 정재하여 리스트에 넣는다.
def readFile(filename):
  with open(filename) as file:
    return [ x.strip('\n').replace(' ','') for x in file.readlines()]
list_ = readFile('1991.txt')

3. 간단한 트리구조로 변환할 함수를 만든다. 4. 트리의 루트를 글로벌로 하나 생성한다.
def my_tree(r):
    return [r, [], []]
root = None

5. 트리구조의 리스트로 변환한다.
def add(data, left, right):
    global root
    if root is None:
        if data != '.':
            root = my_tree(data)
        if left != '.':
            root[1] = my_tree(left)
        if right != '.':
            root[2] = my_tree(right)
        return root
    else:
        return search(root, data, left, right)

def search(root, data, left, right):
    if root is None or root == []:
        return root
    elif root[0] == data:
        if left != '.':
            root[1] = my_tree(left)
        if right != '.':
            root[2] = my_tree(right)
        return root
    else:
        search(root[1], data, left, right)
        search(root[2], data, left, right)


for l_ in list_[1:]:
    add(l_[0],l_[1],l_[2])

6. 전위 후위 중위 탐색 함수를 만든다.
def preorder(root):
    if root:
        print(root[0], end='')
        if root[1]:
            preorder(root[1])
        if root[2]:
            preorder(root[2])

def inorder(root):
    if root:
        if root[1]:
            inorder(root[1])
        print(root[0], end='')
        if root[2]:
            inorder(root[2])

def postorder(root):
    if root:
        if root[1]:
            postorder(root[1])
        if root[2]:
            postorder(root[2])
        print(root[0], end='')

7. 출력한다.
preorder(root)
print()
inorder(root)
print()
postorder(root)
print()

2017년 6월 27일 화요일

[hackerrank][java8][clojure] Simple Array Sum

/* Enter your code here. Read input from STDIN. Print output to STDOUT. Your class should be named Solution. */
Scanner scan = new Scanner(System.in);
int[] i_arr = new int[scan.nextInt()];
for(int i = 0 ; i < i_arr.length; i++) 
  i_arr[i] = scan.nextInt();

System.out.println(Arrays.stream(i_arr).sum());
clojure코드도 한번 써봤다
(use '[clojure.string :only (split trim)])

(let [
  n (Integer/parseInt (read-line))
  arr_t (split (read-line) #"\s+") 
  arr (map #(Integer/parseInt %) arr_t)]
  (println (reduce + arr))   
)
비교를하면
Arrays.stream(i_arr).sum()
이것과
(reduce + arr)
이것의 차이

2017년 6월 18일 일요일

[JBoss] 설치 및 배포해보기

JBoss를 사용해보자. 일단 다운로드 여기에서 zip 파일을 다운받자.(필자는 6.4버전을 받았다.)
그리고 압축을 푼다.

압축을 푼 모습, 여기서 standalone 폴더로 들어가보자.

일단 바로 배포를 해보자. 만약
  1. 일단 가지고 있는 AAA.war파일의 압축을 푼다.
  2. standalone폴더 안에 deployments폴더로 들어간다.
  3. AAA.war라는 이름으로 폴더를 만든다.
  4. 그 안에 압축푼 내용을 넣는다.
  5. Jboss 루트 폴더로 돌아가서 bin/standalone.bat 파일을 실행한다.
  6. 아래 처럼 AAA.tar.deployed 라는 파일이 생성되면 배포 완료!


만약 폴더 안에 내용을 바꾸고 재배포를 하고 싶다면?
  1. standalone/deployments 안으로 들어간다.
  2. AAA.war.dodeploy라는 파일을 생성한다.
  3. AAA.war.isdeploying 이라는 파일이 생성될 것이다.
  4. 위 두 파일이 사라지고 AAA.war.deployed만 남는다면 성공!!

2017년 6월 12일 월요일

[JavaScript] 스코프 제대로 알기 - 3

스코프 제대로 알기 - 3

부제: 함수를 이용한 스코프

function myAlert(index) {
   $("#js-scope3-number"+index).click(function() {
      alert("js-scope3-number" + index + " 번을 클릭했습니다.");
    });
}
$(document).ready(function(){
  var len = 3,
       i;
  for (i = 0; i < len; i++) {
   myAlert(i);
  }
});
보면 알 수 있겠지만 함수 안에서는 새로운 스코프가 연결된다. 다르게 해보자
$(document).ready(function(){
  var len = 3,
       i;
  for (i = 0; i < len; i++) {
    (function (index) {
      $("#js-scope3-number"+index).click(function() {
       alert("js-scope3-number" + index + " 번을 클릭했습니다.");
     });
    }(i));
  }
});
관련페이지 http://www.whynam.com/2017/06/javascript.html http://www.whynam.com/2017/06/javascript-2.html

[JavaScript] 스코프 제대로 알기 - 2

스코프 제대로 알기 - 2

부제: with내용 대충하고 넘어가기

var yhnam = {
  name="yhnam"
  blog="www.iamfrom.kr"
  git="ssisksl77"

with (yhnam) {
    console.log(name);
    console.log(blog);
    console.log(git);
}
실행안해봐도 대충 뭔지 알았을 것이다. 블록 안에 해당 객체가 가지고 있는 변수들을 추가한다. 이걸로 이용해서 문제를 해결하자.
var len = 3,
     i;
for (i = 0; i < len; i++) {
  with ({idx: i}){
    $("js-scope2-number"+idx).click(function() {
      alert("js-scope2-number" + idx + " 번을 클릭했습니다.");
    }
  }
}
     
하지만 이렇게 쓰지 말길.. ECMAScript에서 with쓰지말라고 아예 없애버린듯 하다. 관련페이지 http://www.whynam.com/search/label/javascript http://www.whynam.com/2017/06/javascript-3.html

[JavaScript] 스코프 제대로 알기 - 1

스코프 제대로 알기 - 1

스코프를 왜 공부해야하는가.

이런 코드를 짰다.
var len = 3,
     i;
for (i = 0; i < len; i++) {
  $("#number" + i).click(function() {
    alert("number" + i + " 번을 클릭했습니다."); 
  });
}
아래 3개의 버튼이 있다. id는 number0, 1, 2 이다. 이제 이들을 클릭해보자. 어째서 이런 것일까? 왜 number3 번을 클릭했습니다. 만 뜨는 것일까
scope에 대한 이해가 필요한 시점이다. 자바스크립트에서 스코프에 영향을 주는 3가지 요소
  1. function
  2. with
  3. catch
우리는 이 중에서 function을 많이 살펴볼 것이다. with는 많이 쓰지 않는 듯하고...이제 못 쓸것 같다. 관련페이지 http://www.whynam.com/2017/06/javascript-2.html http://www.whynam.com/2017/06/javascript-3.html

2017년 5월 24일 수요일

[python] 파이썬으로 twilio써보기

twilio는 SMS/MMS를 보내는 서비스이다. 모듈을 받아서 바로 그냥 쓰기만 하면 된다.(아니 물론 회원가입을 하고 인증키를 받은 후)
서비스는 아주 잘 되는 것으로 보이나 문제점은 한국에서 지원이 안된다. (아주 치명적이다.)
그래도 어떻게 쓰는지는 한 번 적어놔야겠다.
from __future__ import unicode_literals
from twilio.rest import Client

account_sid = "AC...내가 받은 아이디"
auth_token = "273 내가 받은 인증코드"

client = Client(account_sid, auth_token)

message = client.messages.create(
    body="Hello, I'm Younghwan Nam. I love you All. See you.",
    to="+8210000000",
    from_="+13399999999"  # 발급받은 twilio 번호
    )

print message.sid

2017년 5월 17일 수요일

[java][bitwise] 자바 비트연산에 대해서...

분명 학원에서 비트연산이라는 건 배워본 적이 없다. 연산이라하면 더하기, 빼기, 곱하기, 나누기, 그리고 모드(%) 뿐이었다.
비트연산을 쓴 적은 자바스크립트를 사용할 때 써본적은 있지만 서버에서 써본 적은 없다.(물론 알게 모르게 자바에서 네트워크 통신같은 것을 할 때 쓰고 있을 것이다.)
비트 연산은 나름 중요해 보인다.
알아두고 가끔씩 써봐야 나중에 남이 만든 코드를 보았을 때, 읽을 용기가 생긴다.
그렇지 않으면 "아 난 안되는가보다." 하고 다른 소스를 찾게 되는 자신을 바라보면서 언제까지 이러면 안되겠다는 생각을 했었다.
static void bitwiseTest4() {
  int seven = 7; // 0000 0111
  int nineteen = 19; // 0001 0011

  // & : and 연산
  // 0000 0111
  // 0001 0011
  // ---------
  // 0000 0011 ->  2 + 1 -> 3
  System.out.println(seven & nineteen);

  // | : or 연산
  // 0000 0111
  // 0001 0011
  // ---------
  // 0001 0111 -> 16 + ( 4 + 2 + 1 ) -> 23
  System.out.println(seven | nineteen);

  // ^ : xor 연산
  // 0000 0111
  // 0001 0011
  // ---------
  // 0001 0100 -> 16 + 4 -> 20
  System.out.println(seven ^ nineteen);

  // ~ : 보수(반전)
  // 0000 0111 -> 1111 1000 -> 1(부호) 111 0000 -> -128 + 64 + 32 + 16 + 8 ->
  System.out.println(-128 + 64 + 32 + 16 + 8);
  System.out.println(~seven);
}

output: 
3
23
20
-8
-8
이걸로 뭘 할 수 있을까? 그건 나중에 알아보기로 해야겠다. 일단 시프트 연산부터 정리를 해야겠다.
시프트 연산은 비트들을 왼쪽 오른쪽으로 움직이면서 노는 것이다. 처음에는 이게 뭐지 싶지만 익숙해지면 괜찮을 것이다. (아마 그럴 것이다. 나는 초보니까 익숙하지 않다.)
static void bitwiseTest5() {
  //시프트 연산
  int ten = 10; // 0000 1010
  int m_ten = -10; // 1
  System.out.println(Integer.toBinaryString(ten));
  System.out.println(Integer.toBinaryString(m_ten));
  // signed right shift : 오른쪽으로 한칸 이동 이전 맨 왼쪽의 숫자에 따라 새로 생성되는 숫자가 정해진다
  // ten : 0000 1010 -> 0000 0101 -> 5
  // m_ten : 1111 0110 -> 1111 1011 -> -1 - 4 = -5
  System.out.print((ten >> 1) + " " + (m_ten >> 1) + "\n");
  // unsigned right shift : 맨 왼쪽 숫자에 상관없이 0으로 통일한다
  // ten : 0000 1010 -> 0000 0101 -> 5
  // m_ten : 1111 1111 1111 1111 1111 1111 1111 0110 -> 0111 1111 1111 1111 1111 1111 1111 1011
  // -> 2147483647(Integer 최대값) - 4 -> 2147483643
  System.out.print((ten >>> 1) + " " + (m_ten >>> 1) + "\n");
  // left shift : 왼쪽으로 이동하는 것이다 right shift의 반대라고 생각하면 된다.
  // 여기서 주목해야 할 점은 right shift는 2배로 줄어들고 left shift는 2배로 커진다는 것이다.(이진수니까 당연한 것이겠지만)
  // 그래서 10번 움직인다면 2의 10승 1024가 곱해진다.
  System.out.print((ten << 10) + " " + (m_ten << 10) + "\n");
  System.out.println((ten << 1) + " " + (m_ten << 1) + "\n");
}

2017년 5월 9일 화요일

[java][design pattern]비지터패턴은 왜 쓰는 것일까? - 2

자 이제 비지터 패턴을 이용하면 무엇이 변하는지 공부하자.
  • Visitor가 Hair들을 방문할 수 있도록 VisitableHair 인터페이스를 만들자.
  • Hair들이 VisitableHair를 구현하도록 하자. (BoldHair, BushyHair, CurlyHair, HairyHair)
  • VisitableHair를 방문할 HairShopVisitor 인터페이스를 만들자.
  • 실제로 방문해서 일을 할 헤어디자이너들을 구현하자. (Barber, Hairdresser, Stylelist)
public interface VisitableHair {
  void accept(HairShopVisitor visitor);
  //아직 HairShopvisitor가 없어서 에러가 남
}
public class BoldHair implements VisitableHair {
  @Override
  public void accept(HairShopVisitor visitor) {
    System.out.println(this + " 아무렇게나 해주세요");
    visitor.visit(this);
  }
  @Override
  public String toString() {
    return "I have a bold Hair";
  }
} 

public class BushyHair implements VisitableHair {
  @Override
  public void accept(HairShopVisitor visitor) {
    System.out.println(this + " 아무렇게나 해주세요");
    visitor.visit(this);
  }
  @Override
  public String toString() {
    return "I have a bushy Hair";
  }
}

public class CurlyHair implements VisitableHair {
  @Override
  public void accept(HairShopVisitor visitor) {
    System.out.println(this + " 아무렇게나 해주세요");
    visitor.visit(this);
  }
  @Override
  public String toString() {
    return "I have a curly Hair";
  }
}
public class HairyHair implements VisitableHair {
  @Override
  public void accept(HairShopVisitor visitor) {
    System.out.println(this + " 아무렇게나 해주세요");
    visitor.visit(this);
  }
  @Override
  public String toString() {
    return "I have a Visitable Hair";
  }
}
이제 HairShopVisitor를 만들자.
public interface HairShopVisitor {
  void visit(BoldHair boldHair);
  void visit(BushyHair busyHair);
  void visit(CurlyHair curlyHair);
  void visit(HairyHair hairyHair);
}
HairShopVisitor를 구현해서 사용하려면 저 위에 있는 모든 객체를 구현 해놔사 사용할 수 있다.
일단 하나를 구현해보자.
public class HairShopBarberVisitor implements HairShopVisitor{
  @Override
  public void visit(BoldHair boldHair) {
    System.out.println("나는 이용사입니다. boldHair 군요. 면도해드릴게요\n");
  }
  @Override
  public void visit(BushyHair busyHair) {
    System.out.println("나는 이용사입니다. busyHair 군요. 밀어드릴게요\n");  
  }
  @Override
  public void visit(CurlyHair curlyHair) {
    System.out.println("나는 이용사입니다. curlyHair 군요. 밀어드릴게요\n");  
  }
  @Override
  public void visit(HairyHair hairyHair) {
    System.out.println("나는 이용사입니다. hairyHair 군요. 밀어드릴게요\n");  
  }
}
이렇게 하면 방문자로서 하나의 로직이 추가된 것이다. 2개를 마저 만들자.
public class HairShopHairdresserVisitor implements HairShopVisitor{
  @Override
  public void visit(BoldHair boldHair) {
    System.out.println("미용사입니다. boldHair 군요. 파마(?)해드릴게요\n");
  }
  @Override
  public void visit(BushyHair busyHair) {
    System.out.println("미용사입니다. busyHair 군요. 파마해드릴게요\n");
  }
  @Override
  public void visit(CurlyHair curlyHair) {
    System.out.println("미용사입니다. curlyHair 군요. 파마해드릴게요\n");
  }
  @Override
  public void visit(HairyHair hairyHair) {
    System.out.println("미용사입니다. hairyHair 군요. 파마해드릴게요\n");
  }
}

public class HairShopHairStylelistVisitor implements HairShopVisitor{
  @Override
  public void visit(BoldHair boldHair) {
    System.out.println("스타일리스트입니다. boldHair 군요. 스타일있게 해드릴게요\n");
  }
  @Override
  public void visit(BushyHair busyHair) {
    System.out.println("스타일리스트입니다. busyHair 군요. 스타일있게 해드릴게요\n");
  }
  @Override
  public void visit(CurlyHair curlyHair) {
    System.out.println("스타일리스트입니다. curlyHair 군요. 스타일있게 해드릴게요\n");
  }
  @Override
  public void visit(HairyHair hairyHair) {
    System.out.println("스타일리스트입니다. hairyHair 군요. 스타일있게 해드릴게요\n");
  }
}
이제 테스트를 해보자.
public static void main(String[] args) {
  VisitableHair[] variable_hair = { new BoldHair(), new BushyHair(), new CurlyHair(), new HairyHair()};
  HairShopVisitor[] artists = {new HairShopBarberVisitor(), new HairShopHairdresserVisitor(), new HairShopHairStylelistVisitor()};
  
  for(HairShopVisitor artist : artists) {
    for (VisitableHair hair : variable_hair )
      hair.accept(artist);
    }
  }
답은?
I have a bold Hair 아무렇게나 해주세요
나는 이용사입니다. boldHair 군요. 면도해드릴게요

I have a bushy Hair 아무렇게나 해주세요
나는 이용사입니다. busyHair 군요. 밀어드릴게요

I have a curly Hair 아무렇게나 해주세요
나는 이용사입니다. curlyHair 군요. 밀어드릴게요

I have a Visitable Hair 아무렇게나 해주세요
나는 이용사입니다. hairyHair 군요. 밀어드릴게요

I have a bold Hair 아무렇게나 해주세요
미용사입니다. boldHair 군요. 파마(?)해드릴게요

I have a bushy Hair 아무렇게나 해주세요
미용사입니다. busyHair 군요. 파마해드릴게요

I have a curly Hair 아무렇게나 해주세요
미용사입니다. curlyHair 군요. 파마해드릴게요

I have a Visitable Hair 아무렇게나 해주세요
미용사입니다. hairyHair 군요. 파마해드릴게요

I have a bold Hair 아무렇게나 해주세요
스타일리스트입니다. boldHair 군요. 스타일있게 해드릴게요

I have a bushy Hair 아무렇게나 해주세요
스타일리스트입니다. busyHair 군요. 스타일있게 해드릴게요

I have a curly Hair 아무렇게나 해주세요
스타일리스트입니다. curlyHair 군요. 스타일있게 해드릴게요

I have a Visitable Hair 아무렇게나 해주세요
스타일리스트입니다. hairyHair 군요. 스타일있게 해드릴게요
몇 번 더 만들어보면 손에 익지 않을까 싶다.

[java][design pattern]비지터패턴은 왜 쓰는 것일까? - 1

비지터 패턴을 쓰는 이유가 뭘까?
왜 방문을 해서 로직을 추가하는 걸까? 그냥 해당 클래스에 로직을 넣어서 쓰면 안될까?
해당 클래스를 바꾸지 못할 때 쓰는 것일까? 해당 클래스들의 자료구조는 변화하지 않을 것이고 로직은 계속 추가될 것이라면 비지터가 좋을 것 같기도 하다.
여기 머리를 자르기로 하자.
곱슬, 직모, 탈모, 대머리, 금발의 긴머리, 뽀글뽀글 등등 여러가지의 머리가 있다. 이들은 모두 미용실에가서 이렇게 말한다.
어울리게 잘라주세요
이제 미용사 성향 / 머릿카락에 따라 머리를 자를 것이다. 구현은
  1. Hair, HairShop 인터페이스 생성
  2. 각 인터페이스의 구현체 생성(BoldHair, BushyHair / Barber, Hairdresser)
  3. 그리고 테스트
// 1. 인터페이스 생성
public interface Hair { }
public interface HairShop {
  void cut(BoldHair boldHair);
  void cut(BushyHair busyHair);
  void cut(Hair hair);
}
이제 인터페이스를 구현하자.
// 2. 구현체 생성
public class BoldHair implements Hair{ }
public class BushyHair implements Hair{ }

public class Barber implements HairShop {
  @Override
  public void cut(BoldHair boldHair) {
    System.out.println("Barber : boldHair");
  }
  @Override
  public void cut(BushyHair busyHair) {
    System.out.println("Barber : BusyHair");
  }
  @Override
  public void cut(Hair hair) {
    System.out.println("Barber : Hair Interface... ");
  }
}

public class Hairdresser implements HairShop{

  @Override
  public void cut(BoldHair boldHair) {
    System.out.println("Hairdressor : BoldHair");
  }

  @Override
  public void cut(BushyHair busyHair) {
    System.out.println("Hairdressor : BushyHair");
  }

  @Override
  public void cut(Hair hair) {
    System.out.println("Hairdressor : Hair Interface... ");
  }
}
이제 테스트를 해보자.
public class Test {
  public static void main(String[] args) {
    Hair[] hairs = {new BoldHair(), new BushyHair()};
    HairShop[] artists = {new Barber(), new Hairdresser()};
 
    for (Hair hair : hairs)
      for (HairShop artist : artists)
        artist.cut(hair);
  }
}
콘솔을 확인
Barber : Hair Interface... 
Hairdressor : Hair Interface... 
Barber : Hair Interface... 
Hairdressor : Hair Interface...  
이렇게 된다. 결국 머릿카락의 정보는 가질 수 없다. 왜냐하면 자바는 싱글디스패치이기 때문인데 자세한 내용은 더블디스패치관련 글을 읽어보기를 바라면서...
여하튼 비지터패턴은 해당 클래스를 변경하지 않고 Visitor들을 추가해서 일을 할 수 있게 한다.
Visitor만 새로 만들면 hair들은 새로운 로직을 추가하지 않고 로직을 추가할 수 있게 된다.
출처:
https://dzone.com/articles/design-patterns-visitor https://sourcemaking.com/design_patterns/visitor/java/1 https://www.tutorialspoint.com/design_pattern/visitor_pattern.htm

2017년 5월 5일 금요일

[java] CSVWriter사용하기 (그리고 쌍따옴표 없애기)

CSV는 참으로 많은 곳에서 사용한다. 데이터를 저장할 때 사용하는 아주 기본적인 포맷이 아닌가 싶다.
CSV에서 오는 간결함은 그것을 만드는 사람마저 간결함을 추구하게 하는 힘이 있다.
콤마으로 나눠진 값들(CSV, Comma Separated Values)은 그 간결함 속에서 더욱 확장되어왔다.

자바에서는 CSV를 만들기 위한 Writer를 따로 만들어주고 있지 않지만, 사람들이 애용하는 라이브러리는 존재한다.
그중 하나 opencsv을 사용해보자.
/** CSVWriter 사용해보기**/
public class CSVTest {
  static List makeFileValues() {
    List list = new ArrayList();
    for(int i = 0; i < 10 ; i++) {
      list.add(new String[] { String.valueOf(i), String.valueOf(i+i), String.valueOf(i+i+i)});
    }
    return  list;
  }
}
실행해보자.
public static void main(String[] args) throws IOException {
  String Path = "C:/ynam/";
  String Name = "CSVTEST";
  
  try (CSVWriter writer = new CSVWriter(new FileWriter(Path+Name + ".csv"))){
    writer.writeAll(makeFileValues());
  }
}
실행해보자.
"0","0","0"
"1","2","3"
"2","4","6"
"3","6","9"
"4","8","12"
"5","10","15"
"6","12","18"
"7","14","21"
"8","16","24"
"9","18","27"
이렇게 나왔다.
이 현상은 사실 우리 회사에서 벌어지던 일이었다. 이 따옴표가 문제는 아니다. 하지만 이 내용을 그대로 다시 디비에 넣고 다시 빼낸다면?
"""0""","""0""","""0"""
"""1""","""2""","""3"""
"""2""","""4""","""6"""
"""3""","""6""","""9"""
"""4""","""8""","""12"""
"""5""","""10""","""15"""
"""6""","""12""","""18"""
"""7""","""14""","""21"""
"""8""","""16""","""24"""
"""9""","""18""","""27"""
처음부터 우리가 원하는 그 값 자체만을 담고 싶다면? 생성자에 추가하나만 하면 된다.
CSVWriter writer = new CSVWriter(new FileWriter(Path+Name + ".csv"))
여기를
CSVWriter writer = new CSVWriter(new FileWriter(Path+Name + ".csv"), ',', CSVWriter.NO_QUOTE_CHARACTER)
이렇게 바꾸고 다시 해보자.
  public static void main(String[] args) throws IOException {
    String Path = "C:/ynam/";
    String Name = "CSVTEST_without_qoute";
  
    try (CSVWriter writer = new CSVWriter(new FileWriter(Path+Name + ".csv"), ',', CSVWriter.NO_QUOTE_CHARACTER)){
      writer.writeAll(makeFileValues());
    }
}
자 실행해보면 어떻게 나올까
0,0,0
1,2,3
2,4,6
3,6,9
4,8,12
5,10,15
6,12,18
7,14,21
8,16,24
9,18,27
단순하다.

2017년 4월 23일 일요일

[java] List에서 int[] 로 바꾸는 방법 (on java8)

List에서 int[]로 바꾸는 손쉬운 방법은 없었다. Arrays의 toArray()는 Object[]를 반환하기 때문에 불가능했고
toArray(T[] ...) 얘도 primitive type인 int[]를 담을 순 없었다.

하지만 Java8에서는 쉽게 변환할 수 있다.
List list = new ArrayList<>();
...
int[] i_arr = list.stream().mapToInt(i -> i).toArray();
어떻게 이렇게 쉽게 되는 걸까?
list.stream()은 게다가 아직 Integer이다. 무슨 일이 벌어진 걸까?
java8부터 primitive type을 다룰 수 있는 IntStream이라는 것이 있다.
우리는 이놈으로 바꿔야만 한다. 그래서 사용하는 것이 mapToInt이다 얘는 IntStream을 리턴한다.
mapToInt이라는 이름처럼 요소 하나하나를 매개변수 안에 있는 로직대로 이행한 후 IntStream으로 리턴하는 듯하다.

그래서 실제로는 이렇게 되야 한다.
mapToInt((Integer i) -> i.intValue()) or mapToInt(Integer::intValue())
Integer에 있는 메소드 intValue로 int로 변환하는 함수를 넣는 것이 정상이다. 그런데 왜 그냥 아무짓도 안해도 에러가 안나고 잘 되는 걸까?
바로 언박싱을 이용하는 것인데, 리턴값은 int로 정해져있고 컴파일러가 그걸 확인할 것이다.

만약 자바8을 쓸 수 없다면?
Guava를 사용하면 되겠다.

[java][design pattern] 더블 디스패치 패턴은 언제 쓰일까?

더블디스패치 패턴은 대체 왜 쓰이는 걸까?
자바는 싱글디스패치만을 지원한다고 한다.
그 말은 더블디스패치를 사용하려면 패턴으로 메꿔야 한다는 말인가보다.
예전에는 싱글디스패치만으로도 만족하고 살았지만 다른 많은 언어들이 실행시키는 인스턴스와 매개변수 둘 다 타입체크를 하여 멋져졌다 하니.
우리도 패턴으로 그것이 가능함을 보여야 하지 않겠나 해서 만들어 진 것이 아닌가 하는 상상을 해본다.

일단 만들어보자. 이제부터 할 일은 이렇게 된다.
  • 인터페이스 둘을 만든다. (Format, Post)
  • 인터페이스를 구현한다. (Post - Mail,SMS / Format - JPG,XML)
  • 이 두 인터페이스를 연결한다.
  • 실행해본다.
  • 일단 왜 더블디스패치가 없이 만들면 어떻게 되는지 보자.
    1. 인터페이스 둘을 만든다.
    interface Format {}
    interface Post {}
    
    2. 인터페리스를 구현한다. (총 4개)
    public class JPG implements Format { }
    public class XML implements Format { }
    
    public class Mail implements Post { }
    public class SMS implements Post { }
    
    이제 인터페이스에 필요한 내용들 추가하고 구현도 해서 이 두 인터페이스를 연결하자.
    Post인터페이스는 JPG / XML에 따라 보내는 방식이 다를 수 있다. 그래서 그 타입에 따라 다른 일을 하도록하자.
    public interface Post {
      void send(Format format);
      void send(JPG jpg);
      void send(XML xml);
    }
    
    게다가 SMS/MAIL에 따라 다를 것이다.
    public class Mail implements Post {
    
      @Override
      public void send(Format format) {
        throw new RuntimeException("Mail.send(Format format) :: 타입을 알 수 없습니다.");
      }
    
      @Override
      public void send(JPG jpg) {
        System.out.println("Mail.send(JPG jpg)");
      }
    
      @Override
      public void send(XML xml) {
        System.out.println("Mail.send(XML xml)");
      }
    
    }
    
    public class SMS implements Post {
    
      @Override
      public void send(Format format) {
        throw new RuntimeException("SMS.send(Format format) :: 타입을 알 수 없습니다.");
      }
    
      @Override
      public void send(JPG jpg) {
        System.out.println("SMS.send(JPG jpg)");
      }
    
      @Override
      public void send(XML xml) {
        System.out.println("SMS.send(XML xml)");
      }
    
    }
    
    다 되었다. 실행해보자.
    public class Test {
      public static void main(String[] args) {
        Post post = new Mail();
        Format format = new JPG();
        post.send(new JPG());
        post.send(format);
      }
    }
    
    잘 될까?
    Mail.send(JPG jpg)
    Exception in thread "main" java.lang.RuntimeException: Mail.send(Format format) :: 타입을 알 수 없습니다.
     at designpattern.doubledispatch.post.Mail.send(Mail.java:11)
     at designpattern.doubledispatch.Test.main(Test.java:13)
    
    하나는 잘 되었고, 다른 하나는 잘 되지 않았다. 왜냐하면 메소드 오버로딩은 정적 디스패치(static dispatch)를 한다. 이 말은 런타임에 타입을 동적으로 체크하는 것이 아니라 미리 컴파일 이전에 타입을 미리 제대로 정해 놔야 한다는 말이다.
    에러가 난 곳은 정확한 타입(JPG)를 넣은 것이 아니라. Format이라는 인터페이스를 넣었다. 이것이 문제가 되는 것
    그러면 어떻게 해야 할까?
    일단 자바에서는 매개변수 타입체크를 동적으로 하지않는다고 하니까 타입체크를 두 번 하도록 해보자.
    지금부터 할일은
    1. Post 인터페이스를 매개변수가 Format인 것만 남기고 삭제한다. (어짜피 이걸로 타입체크를 못함)
    2. Format 인터페이스에 Mail,SMS(Post의 구현체 전부)를 각각 매개변수로 하여 void send(T t)형식의 메소드를 추가한다
    3. Post의 구현체에서 해당 메소드를 호출한다.
    4. 실제로 로직을 실행하는 곳은 Format의 구현체들에서 각각 구현한다.
    말이 잘 전달되지 않은 것 같은데 일단 코드를 한 번 짜보자.
    // 1. 매개변수가 Format 인것 빼고 다 삭제
    public interface Post {
      void send(Format format);
    }
    // 2. Mail,SMS를 각각 매개변수로 send라는 메소드를 추가
    public interface Format {
      /*
       * 여기에서 타입체크를 하는 것이다. 원래는 Post에서 했지만
       * 한 번 더 하기 위해, Format에서 한다.
       * 
       */
      void send(Mail mail);
      void send(SMS sms);
    }
    
    이제 인터페이스를 바꿨으니 구현체들을 바꿔보자.
    // 3. 새로 만든 메소드 호출
    public class Mail implements Post {
    
      @Override
      public void send(Format format) {
        format.send(this); // 여기서 이미 this는 타입체킹이 되어있음.
      }
    }
    
    public class SMS implements Post {
    
      @Override
      public void send(Format format) {
        format.send(this);
      }
    
    }
    
    이제 실제 로직이 들어있는 곳을 보자.
    // 4. 실제 로직이 있는 곳 변경
    public class JPG implements Format {
    
      @Override
      public void send(Mail mail) {
        System.out.println("mail send JPG");
      }
    
      @Override
      public void send(SMS sms) {
        System.out.println("SMS send JPG");
      }
    
    }
    
    public class XML implements Format {
    
      @Override
      public void send(Mail mail) {
        System.out.println("mail send XML");
      }
    
      @Override
      public void send(SMS sms) {
        System.out.println("SMS send XML");
      }
    
    }
    
    한번 실행해보자.
    public class Test {
      public static void main(String[] args) {
        Post post = new Mail();
        Format format = new JPG();
        post.send(new JPG());
        post.send(format);
      }
    }
    
    과연 결과는??
    mail send JPG
    mail send JPG
    
    성공이다. 이 패턴은 비지터 패턴과 아주 유사하다고 하는데, 비지터 패턴은 내 생각에는 클래스를 변경하지 않고 새로운 로직을 추가하기 위해 필요한 패턴인 것 같고
    더블디스패치패턴은 좀 더 일반적인 기술같다. 양쪽의 타입을 제대로 체크하기 위해 만든 기술인듯하다.

    [java] interface constant는 왜 쓰면 안될까?

    가끔 이런 생각을 해보기도 한다.
    귀찮은데 그냥 상수도 인터페이스로 넣어서 쓰면 안되나?
    아니면 implements로 상수를 전부 가져와서 이름만으로 써도 되지 않을까?
    아래의 예시가 왜 쓰면 안되는지 도움이 될 것 같다. 우선 클래스 안에 있는 상수들을 보자.
    public class ClassCons {
     public static final double CONS = 0.0;
     public static final String CONS1 = "22";
     public static final int CONS2 = 1;
    }
    
    이제 이것들을 인터페이스 상수로 바꿔보자.
    interface InterCons {
     double CONS = 0.0;
     String CONS1 = "22";
     int CONS2 = 1;
    }
    
    인터페이스는 어짜피 public이다. 그러니 public이 필요없다. 바뀌지 않을 것이니 final도 필요없다.
    마치 짧아진 코드를 보며 내가 뭔가 해낸(?)사람 처럼 느낄 수도 있지만 아님을 바로 깨달을 수 있다.
    public class BadIdea implements InterCons{
     public BadIdea() {
      System.out.println(CONS);
      System.out.println(CONS1);
      System.out.println(CONS2);
     }
     public static void main(String[] args) {
      BadIdea badIdea = new BadIdea();
     }
    }
    
    이 내용은 위에서 만든 인터페이스 상수를 가져온 것이다. 과연 이렇게 하면 잘 될까?
    0.0
    22
    1
    
    예상한 대로 나왔다. 하지만 이렇게 한다면?
    public class BadIdea implements InterCons{
     private int CONS = 100;
     public BadIdea() {
      System.out.println(CONS);
      System.out.println(CONS1);
      System.out.println(CONS2);
     }
     public static void main(String[] args) {
      BadIdea badIdea = new BadIdea();
     }
    }
    
    다시 실행해보자.
    100
    22
    1
    
    값이 바뀌어 있다. 이 말은 인터페이스 상수를 가져오는 순간 해당 클래스와 그 서브 클래스들이 상수들로 더러워(?)진다는 것을 말한다.
    자칫하면 값들이 꼬일 수 있다. 에러도 잘 보이지 않을 거고 디버깅에도 애를 먹을 수도 있겠다.

    2017년 4월 20일 목요일

    [java] volatile이란 뭘까?

    Dzone에서 volatile내용을 보고 있다가 좀 적어놔야 할 것같아서 적는다.
    자바의 keyword 중에 하나인 volatile은 대체 정확히 어떨 때 쓰는 것일까?
    애초에 volatile은 뭘까?
    volatile은 휘발성 이란 의미를 가지고 있다. 잘 날아간다는 말이니까. 금방금방 그 값이 사라진다고 말 할수도 있고, 그 값이 의미없어진다고도 말 할수 있겠다.
    volatile은 값을 읽고, 쓰는 것에 대한 원자성(Atomic)을 유지해주는 것 같다.

    왜 이게 필요한가 하면 멀티스레드 환경에서 스레드 각각이 변수들의 값을 캐싱해서 사용한다는 것때문이다. 덕분에 값을 조회할 때마다 실제 값을 조회할 필요가 없이 사용할 수 있지만 만약 공유하는 값이 있고 그 값이 자주 변한다면 문제가 생길 수 있지 않을까?
    그때 volatile을 사용하면 되지 않을까 싶다.
    
    public class VolatileTest {
      private static volatile int count = 0;
    //private static int count = 0;
      public static void main(String[] args) {
        new IwillGetIt().start();
        new IwillChangeIt().start();
      }  
      
      static class IwillGetIt extends Thread {
        @Override
        public void run() {
          int my_local = count;
          while (my_local < 5) {
            if (my_local != count){
              System.out.println("바꼈다!!! : " + count);
       my_local = count;
     }
          }
        }
      } 
      static class IwillChangeIt extends Thread {
        @Override
        public void run() {
          int my_local = count;
          while (my_local < 5 ) {
            System.out.println("값을 증가 시킴 : " + (++my_local));
            count = my_local;
            try {
              Thread.sleep(2000);
            } catch (InterruptedException e) {
       e.printStackTrace();
     }
          }
        }
      }
    }
    
    어떻게 될까? 위의 내용은 volatile을 넣은 것이다. count라는 값은 두개의 스레드가 공유를 한다. 서로 IwiilChangeIt은 바꾸는 아이고 IwillGetIt은 바꾸는 현장을 포착하는 아이다.
    값을 증가 시킴 : 1
    바꼈다!!! : 1
    값을 증가 시킴 : 2
    바꼈다!!! : 2
    값을 증가 시킴 : 3
    바꼈다!!! : 3
    값을 증가 시킴 : 4
    바꼈다!!! : 4
    값을 증가 시킴 : 5
    바꼈다!!! : 5
    
    그렇다면 이 상태에서 저 위에 volatile이 없는 녀석의 주석을 풀고, volatile이 있는 녀석은 주석을 걸어보고 다시 실행하자.
    값을 증가 시킴 : 1
    바꼈다!!! : 1
    값을 증가 시킴 : 2
    값을 증가 시킴 : 3
    값을 증가 시킴 : 4
    값을 증가 시킴 : 5
    
    그렇다면 volatile은 항상 쓰는 것이 스마트한 것일까?
    난 잘 모르겠지만 그렇게는 쓰지 않는 것이 좋을 것 같다. 왜냐하면?
    1. volatile은 변수에게 특권을 주는 것과 같다. 그 특권을 남발하면 안된다.
    2. volatile을 모든 변수에게 주면 코드가 지저분하다.
    3. 1번과 일맥상통한 것인데 volatile을 죄다 쓰면 누가 진짜 필요한 volatile인지 누가 알 수 있을까. 왜 이렇게 썼는지 고민하고 있지 않을까?
    4. 성능의 이슈는 없을까? 하지만 이건 아직 내가 신경쓸 것은 아닌 듯 하다.

    2017년 4월 13일 목요일

    [JavaScript] 자바스크립트에서 private변수 사용하기

    자바스크립트에서는 private을 넣는다고 private 변수가 되지 않는다.
    var my obj = {
      myprop: 1,
      getProp: function() { return this.prop; }
    }
    console.log(myobj.myprop);
    1
    console.log(myobj.getProp()); 
    1
    
    다른 방법으로도 한번 만들어보자.
    function G() {
      this.name '지';
      this.age = function() { return 1; }
    }
    var g = new G();
    console.log(g.name);
    '지'
    console.log(g.age());
    1
    

    Solution

    자바스크립트는 별도의 syntax가 없어도 private변수를 만들 수 있다.
    function new_G() {
      var name = 'G의 숨은 뜻은 지메일'; // private
      this.getName = function() { return name; };
    }
    var secret_g = new new_G();
    console.log(secret_g.name);
    undefined
    console.log(secret_g.getName());
    'G의 숨은 뜻은 지메일'
    
    생성자가 함수의 클로저를 만들고 그 클로저 스코프 안에 속하는 변수들을 만든다. closure scope는 생성자 외부로 노출되지 않는다.

    2017년 4월 12일 수요일

    [java][thread] join을 이용하여 thread가 끝났는지 확인하기

    join()을 이용하여 thread가 끝났는지 확인하자.

    오늘 이런 형태의 소스를 보았다. (아래 내용은 예시)
    RunnableClass01 rc01 = new RunnableClass01();
    RunnableClass02 rc02 = new RunnableClass02();
    RunnableClass03 rc03 = new RunnableClass03();
    
    Thread th01 = new Thread(rc01);
    Thread th02 = new Thread(rc02);
    Thread th03 = new Thread(rc03);
    
    th01.start();
    th02.start();
    th03.start();
    
    DB.updateThreadEndsTime(); // 이것이 문제
    
    대충 이런식이었는데... 쓰레드는 끝나지도 않았지만 시작하자마자 스레드가 끝났다는 로직을 실행시킨다.
    전체 소스를 보지 않은 나의 탓이 크지만
    쓰레드가 완료되었다는 시간을 처리하는 로직은 '당연히' 쓰레드가 전부 끝났을 때 업데이트 할 것이라는 가정이 있었기에 로직 수정 후 낭패를 보았다.
    그 후 나는 동료에게 물었다.
    "왜 이렇게 해야 하죠? 쓰레드가 끝나고 완료 업데이트를 하면 안되나요?"
    "쓰레드는 원래 그래요. 서로 독립적으로 실행되기 때문이죠."
    나는 단순하게 join()을 사용해서 그것이 가능함을 보이려 했다.
    RunnableClass01 rc01 = new RunnableClass01();
    RunnableClass02 rc02 = new RunnableClass01();
    RunnableClass03 rc03 = new RunnableClass01();
    
    Thread th01 = new Thread(rc01);
    Thread th02 = new Thread(rc02);
    Thread th03 = new Thread(rc03);
    
    th01.start();
    th02.start();
    th03.start();
    
    try {
      th01.join();
      th02.join();
      th03.join();
    }catch (Exception e) { }
    
    DB.updateThreadEndsTime(); // 이제 스레드 전부 완료되면 된다.
    
    "이러면 쓰레드 하나가 끝나면 다른 하나가 시작되는 것 아녜요?"
    나는 말했다.
    "아닙니다. 시작은 같이 하고 끝나는 것만 동기되는 거예요.
    어짜피 다 끝나야 완료처리 되야 하는 거니까 다 끝나야 일을 하는것이 맞다고 생각해요." 이렇게 끝났다. 하지만 여기서 하나 더 수정을 해야겠다.
    Thread th01 = new Thread(new RunnableClass01());
    Thread th02 = new Thread(new RunnableClass02());
    Thread th03 = new Thread(new RunnableClass03());
    
    th01.start();
    th02.start();
    th03.start();
    
    try {
      th01.join();
      th02.join();
      th03.join();
    } catch (Exception e) { }
    
    DB.updateThreadEndsTime(); // 이제 스레드 전부 완료되면 된다.
    
    물론 다른 방법도 많겠지만, 실력의 부족과 상황이 여의치 않아 이렇게 단순하게 바꾸는 것 만으로도
    로직의 큰 변화가 왔다.
    join(), wait(), notify() 같은 것은 기본적으로 알아야할 기능 같다.

    2017년 3월 28일 화요일

    [시간의향기]를 읽고

    전작 [피로사회]를 수년 전에 읽었었다. 대학교 시절 독서토론을 하면서 이 강렬한 내용을 보면서 모두가 놀랬었다. 아주 짧은 글인데 한 장 한 장 넘기는 종이가 무겁다. 들어 올리다가도 이내 내리고 다시 읽는다. 글 자체에 대한 이해가 힘들었다. 원래 책을 읽으면서 이해가 안되면 책 탓을 하지만, 이 책은 나의 탓이라는 주문을 걸면서 인터넷으로 단어들을 검색하며 읽고 또 읽었다.

    그 후 한병철이라는 철학가의 다른 책을 꼭 읽기로 했었는데, 이제야 [시간의 향기]라는 책을 구매했다. 역시나 시집보다 작은 책임에도 내 마음은 '언제 이 다 읽나' 하는 무거운 마음이 들었다. 한 장 넘기다가 다시 돌아오고, 한 문장 한 문장 곱씹다가 책을 완독했지만, 작가가 말하는 세계관을 완벽하게 이해하지 못했다. 분명 나의 부족함이라 생각하기에 나중에 좀 더 시간이 지나서 다시 읽기로 하며 책을 덮었다.

    [시간의 향기] 내용

    세상은 너무 빨라서 이제는 역사가 만들어지지 않고 있다. 세상이 너무 빠르게 나아가기 때문에 우리는 따라잡기 바쁘다. 우리는 숨이 턱밑까지 차오르게 하는 속도 때문에 시간의 향기를 맡을 수 없다.

    느리게 걷기나 느린 삶을 추구하려는 것, 이런 것들은 이 빠른 세상 속에서 살아남기 위한 치료법이 아닌 증상일 뿐이다. 우리는 빠르게 나아가는 시간을 잡을 방법이 없는 것 같다. 시간은 적당히 느려야 세상에 그 발자취가 새겨지고 우리는 그것을 기억하게 되고 되짚고 반성하고 진화한다. 그리고 세상이 빨라졌다고 우리 인생이 편해진 것은 아니다. 이것이 우리 마음의 조급함을 대신해주지 않는다. 우리는 그 속도를 따라잡으려 애쓰고, 그 위에 올라타 더 조급해진다.

    우리가 가는 길의 향기, 시간의 향기를 맡을 세도 없이 우리는 이곳저곳으로 갈 뿐이다. 우리는 멈춰야 하며, 아무것도 하지 않음으로 바로 보아야 한다. 그리고 사색해야 한다.
    노동은 자유롭게 만들지 않는다. 일의 명령은 새로운 노예사회를 낳는다.

    노동을 멈추고 사색하는 삶은 가지자.

    2017년 3월 22일 수요일

    [타르튀프]를 읽고

    열린책들 세계문학서적 중에 아주 특이한 이름의 책을 보았다. [타르튀프]라는 글인데 이 책이 나를 이끈 이유는 머리말을 읽고 나서였다. 글쓴이 몰리에르는 신에 대한 내용도 과감히 희극의 소재로 사용해왔는데, 권력자들을 소재로 사용할 때부터, 그의 서적이 불온서적이 되며, 무대에 올릴 수 없는 등 갖가지 제약을 받게 된다. 머리말에는 그들에 대한 작가의 절제된 분노를 느낄 수 있는데, 그것은 권력자들을 향한 질타였다.
     

    [타르튀프]는 이 책에 실려있는 세 편의 희극 중 하나이다. 아주 옛날에 쓴 희극임에도 호흡이 현대와 맞다. 아주 빠르다 못해 가파른 느낌이 준다.분명 그 당시에는 쉴세 없이 웃다보니 막이 내려오는 느낌이 있었으리라.
    권력자들은 자신들의 악행을 비난하는 것은 참지만
    놀림감이 되는 것은 참지 못한다.
    이 바로 위에 있는 글은 [타르튀프]의 머리말에 나오는 문장 중 하나이다. 희극의 중요성은 웃음 속에 감춰진 무엇이라는 것. 웃음으로 밖에 해소할 수 없는 그 무엇을 아주 잘 다룬 작품이었다. 특히 귀족들의 위선된 행동을 풍자한 [타르튀프]라는 희극은 참으로 많은 생각을 하게 해준다.



    권력자에게 칼을 휘두를 수 없지만, 그들을 깍아 내릴 수는 있다.
    그들을 저 위에 있지 않으며, 우리와 같은 사람이고, 때론 우리보다 아래 있다. 그들이 가진 권능, 신의 축복은 그들이 대단해서가 아니며, 사실 그들이 가진 권능은 신이 준 것이 아닌 우리의 두려움과 그들의 힘에서 나온다. 그들을 희극으로 놀림감으로 만들어 우리 앞에 세우고 심판하도록 하자.


    내가 느낀 감상이다. 하지만 몰리에르는 지속적인 압박에 지쳤는지 [타르튀프]의 마지막 희극 [인간 혐오자]에 그의 안타까움, 외로움이 느껴진다.


    [linux] ssh은 되는데 sftp가 안되는 경우

    sftp root@123.123.123.123
    root@123.123.123.123's password:
    subsystem request failed on channel 0
    Connection closed
    

    그런데 ssh은 잘 들어가진다면?
    뭔가 설정을 잘못되어 있을 수 있다. 일단 이걸 열어보자.
    #vi /etc/ssh/sshd_config
    
    그리고 sftp를 찾아보자. /sftp-server로 해당 내용을 검색하니 맨 마지막에 내용이 있다.
    없으면 넣어주도록 하자.
    #override default of no subsystems
    Subsystem    sftp    /usr/libexec/sftp-server
    
    이런게 적혀 있어야 한다. 그런데 왜 안되었을까.
    해당 패스에 가보기로 하였다.
    #/usr/libexec/openssh/s
    sftp-server    ssh-keycat    ssh-keysign    sh-pkcs11-helper
    #/usr/libexec/openssh/s
    
    뭔가 특이점이 보이는가? 패스가 틀려 있어서 sftp가 안되었던것!! 어서 바꿔주고 재시작을 해보자.
    # service sshd restart
    
    이제 잘 될겁니다.ㅎㅎ

    2017년 3월 8일 수요일

    [java][junit] private인 메소드는 어떻게 테스트를 해야 하나요?

    오늘 JUNIT테스트 케이스를 작성하려고 하는데 이상한 것을 봤습니다.
    바로 제가 테스트할 메소드가 JUNIT 테스트 케이스 안에 들어있는 것이었습니다.
    상황이 뭐냐하면
    다른 누군가가 해당 메소드가 privat으로 되어 있었기 때문에 JUNIT에서 불러 올 수 없었던 것이지요. 그래서 똑같은 메소드를 만들어서 테스트를 했던 것입니다.
    문제는 이 테스트가 나중에 쓸모 없어진다는 것같아요.
    테스트를 나중에 돌려도 잘된다고 나오겠죠. 버그가 있어도
    그러면 JUNIT은 어떻게 테스트를 해야 하는거지?
    간접적인 테스트를 하는 것이 방법일 것 같아요. private메소드를 사용하는 public 메소드는 최소한 하나는 있을 것이니 그것을 이용해서 테스트를 하는 것입니다.
    하지만 그것으로 부족하다고 느끼시다면?
    방법이 있습니다. 리플렉션을 사용하는 방법이죠.
    코드를 보겠습니다.
    import java.util.List;
    
    public class A {
    	private String name;
    	public A (String name) { this.name = name; }
    	
    	private String dosthPrivate(int a, String b, List list) {
    	  return "name: " + this.name + 
                ", a: " + a + ", b: " + b + ", List: " + list;
    	}
    }
    
    현재 위에 있는 dosthPrivate은 실행할 방법이 없습니다.
    JUnit으로 테스트를 하려면 어떻게 해야 할까요??
    이제 테스트 코드를 보도록 합니다.
    public class ATest {
      A a = new A("TEST");
      @Test
      public void test() throws Exception{
      Method method = 
        A.class.getDeclaredMethod("dosthPrivate", int.class, 
                                  String.class, List.class);
        method.setAccessible(true);
        String result = String.class.cast(method.invoke
          (a, 1, "BBB", new ArrayList(Arrays.asList(1,2,3,5,8,13,21))));
        assertThat(result, CoreMatchers.is
          ("name: TEST, a: 1, b: BBB, List: [1, 2, 3, 5, 8, 13, 21]"));
      }
    }
    
    아주 간단하죠?

    아주 간단합니다.

    2017년 2월 19일 일요일

    [clojure] Udemy 클로저 강의를 듣고

    이 언어는 나에게 많은 시련을 준 언어이다.
    물론 자바를 처음 배울 때 처럼 아침부터 저녁까지 공부한 것은 아니지만 참으로 나의 머리의 한계를 시험하는 언어였다.
    클로저를 처음 배우려고 했던 이유는 폴 그레니엄 때문이었다. 내가 일단 이 사람을 어떻게 접하게 되었는지를 적어야 겠다.
    나는 문예창작학과로써 서울에 올라온 이후 항상 서점에 갔다.
    삼성동에서 학원을 다닐 때에는 코엑스 지하 1층에 있는 영풍문고를 갔다. 매일매일 저녁을 먹지 않고 혹은 저녁 9시에 학원이 끝났을 때, 나는 영풍문고에서 나의 무지 혹은 나의 미래에 대한 불안감, 거기서 오는 허기짐을 문고에서 채웠다.
    아무것도 모르면서 계속 읽었다.
    그 중에 [해커와화가]라는 책이 있었다.
    제1장을 읽고, 다음장을 읽으려 할 때, 문고가 닫는다는 알림과 음악이 흘러나오기 시작했다. 나는 그 책을 사서 집으로 가져가 전부 읽었다.
    그의 주장에는 확신이 있었고, 또 그 확신이 허세가 아니라는 것을 이 책 하나만으로 짐작할 수 있었다.
    나는 그의 홈페이지를 들어가보았다. 그는 리스프형 언어 Arc를 소개하고 있었다.
    리스프형이 무엇인지 궁금하여 lisp에 대해 찾아보기 시작했다.
    그리고 scheme를 한 번 사용해보았다.
    common lisp도 한 번 만져보았다. (정말... 만져만 보았다)
    내가 클로저를 공부한 이유는 다름이 아니라. 내가 보기에 한국 책이나, 외국 원서나 clojure에 대한 책이 더 많아 보였기 때문이다.
    꽤나 많은 책을 보았지만 아직도 clojure에 대해 익숙하지 않다. 내가 일로써 사용하지 않아서 그런 것 같기도 하다.
    아직 나는 클로저에서 async에 대해서는 전혀 모른다.(구경만했을뿐...)
    병렬프로그래밍이라하면 atom, refer 정도 사용할 뿐일 듯 싶다.
    한국 책이던, 외국 책이던 정보가 부족한 것은 참으로 안타까운 듯 싶다. 그래서 사람들이 클로저를 사용하는 듯 싶다.
    그나마... 자바와 연결이 되어 있어서, nslookup이라던가. 여러 네트워크 프로그래밍에 자바의 메소드를 사용하는 경우가 있는 것같다.
    나는 이제 클로저에서 벽에 부딪힌 느낌이다. 뭐 아는 것도 없는데 벽이라니... 그래서 기초라도 제대로 다지자고 Udemy에 있는 강의를 구매했다.

    나름 괜찮았던 것 같다. 하지만 더 많은 정보가 있으면 좋을 텐데... 일단 리스프형 언어에 대해서도 아직 이해가 부족한 상태에서 미지의 세계로 나아가려니 참으로 답답하지 짝이없다.


    그래도 고민으로 끝나지않고 계속 나아갈 것이다. 이 길이 망하는 길이라 할지라도
    배움은 새로운 곳을 나아가기 위한 것이 아니라
    같은 곳에서 새로운 것을 바라보기 위한 것이라 믿으니까.
    어느날 다시 되돌아와도...
    난 달라져 있으리라

    2017년 2월 18일 토요일

    [java][byte] 0xff 누구냐 넌!

    제목에만 적어놓고 언젠가 적어야지 적어야지 했던 글을 이제야 적는다.
    자바를 사용할 때 가끔 0xff를 사용해야 할 때가 있다. 없다고? 음... 그렇다면 당신 대신 누군가 해준 것이니 모른다는 것에 감사하시길...

    혹시 unsigned int와 singed int를 아는지? 아아 아주 단순한 문제이니 대답을 해주겠다.
    unsigned int : 사인(부호)이 안되어있는 int값
    signed int : 사인(부호)이 있는 int값
    좀 더 자세히 말하면 부호가 있는 int값과 없는 값 둘로 나뉜다. 그렇다면 자바에서 사용하는 int는 무슨 값일까?

    ???
    모를 수도 있고 알 수도 있다. 비전공자인 나로썬 뭔말인가 싶었다. 하지만 다 정보처리기사에 나오니 걱정하지말라. 누가 물으면 이렇게 말해라.
    '그거 정보처리기사에서 다 공부했죠.'
    자바에는 int건 byte건 unsigned란 없다. signed만을 (이렇게 해서 설계를 잘했다고 하는 강의도 본 적이 있는데 난 허접이라 잘 모르겠다.) 사용한다. 맞다. 부호가 있는 숫자를 사용한단 말이다. 부호가 없는 int는 사용하지 않는다. 뭐야? 그럼 우리가 알 필요가 없잖아??
    유감스럽게도 아니다
    왜냐하면 세상은 unsigned int로 이루어진 것들이 꽤나 있기 때문이다. 일단 그렇게 사용하던 코드들이 있고 가장 중요한 것은 이 값은 바이트 타입으로 받았을 때이다.
    한 번 바이트 타입으로 127 이상으로 값을 넣어보자.
    public static void main(String[] args) {
      byte[] a = new byte[] {127, 0, 0, 1};
      byte[] b = new byte[] {128, 0, 0, 1};
    }
    
    a는 되고 b는 안된다. 삶과 죽음은 종이한장 차이라더니, 1의 차이가 프로그램을 아예 실행할 수 없게 만들고 있다.
    왜그럴까?
    바로 자바는 항상 부호가 있는 숫자이기 때문이다.
    바이트는 8비트다. 8비트이면 256개를 표현할 수 있는데, 왜 128이 안된다는 것일까?
    잘 생각하자 우리는 부호가 있는 값을 이용한다.
    256을 반으로 뚝 잘라라


    128 | 128 오!! 되잖아!!! 라고 말 할 줄 알았다. 여기서 한 놈은 1을 빼야한다.
    왜? 0을 누군가는 표기해야 하니까.
    지금은 그래서 0000 0000 이놈이 0을 표현하게 되는데 이 0이 누구꺼냐는 거다.
    결론을 말하면 자바 바이트 타입은 -128 ~ 127 이렇게 표현할 수 있다. 그러니 128은 표현할 수 없는 존재가 되는 것이다.
    public static void main(String[] args) {
      byte[] b = new byte[] {(byte)128 , 0, 0, 1};
      System.out.println( (byte) 128 );
    }
    
    이거 마이너스가 뜬다. 왜냐하면, 128은 int다. 그걸 바이트로 내가 지금 강제로 형변환을 했다. 128은 2의 7승이다. 그 말은 1000 0000 이다.

    일단 맨 앞에 1이 있으면 부호가 음수이다.

    public static void main(String[] args) {
      byte[] b = new byte[] {(byte)128 , 0, 0, 1};
      System.out.println( (byte) 128 );
      System.out.println((byte) 128 & 0xff);
    }
    
    바로 우리가 알아봐야 할 0xff 누구냐넌! 이놈은 이렇게 생겼다. 쟤는 16진수라는 거다.
    잠깐 나가지말고 들어보라. 정말 별거 아니니까!!!
    0x는 16진수 앞에 붙는 기호라고 하고.
    f는 15을 뜻한다.
    f가 두개다. 이것 이런 말이다. 15*16^1 + 15*16^0 이거 한번 계산기를 돌려보자.
    System.out.println(15*16 + 15*1);
    
    이건 255가 나온다. 255? 어디서 많이 들어 본 것 같지 않은지? 맞다. 우리 네트워크 설정할 때 많이 사용한다. 바로 그 때!!!! 사용되고 있을 지도 모르는 놈이 0xff이다.

    여튼 이놈을 이용해서 &(and)연산을 하면 부호가 없는 없는 그냥 숫자가 나올 것이다. (왜?)
    잘 생각해보자. 255는 일단 부호가 있는 숫자에서 255라는 숫자가 나온것이다.
    그러면 잘 생각해보자.
    기호가 없는 255는 1111 1111 이다. 기호가 있는 255는? 무조건 앞이 0이다. 게다가 255는 int라니까? 부호가 있는 32비트다. 32비트!!!
    0000 0000 0000 0000 0000 0000 1111 1111
    0xff의 정체이다. 자 그런데 어떻게 저 (byte) 128 & 0xff 가 어떻게 되는거지?
    자!! 이제 얼마 안남았다.
    1111 1111 1111 1111 1111 1111 1111 1111 1000 0000
    0000 0000 0000 0000 0000 0000 0000 0000 1111 1111 (and) 연산 실시
    ----------------------------------
    0000 0000 0000 0000 0000 0000 0000 0000 1000 0000
    
    이러면 이제, 부호가 마이너스가 아니라! 부호가 플러스인 128라는 숫자가 된다.
    휴... 금방 끝날 줄 알았는데... 하... 힘들었다.


    이 글이 정말 서툴지만 누군가에게는 많은 정보가 되길 바랍니다. 그리고 잘못 적은 내용을 지적해주신 분께 감사의 뜻을 전합니다. ^^

    2017년 1월 26일 목요일

    [clojure] 클로저 소개글

    사내 블로그를 적어야 할 일이 있어서(할당량...) 무엇을 적을 까 하다가
    클로저에 대해 한 번 써보았다.
    좀 약파는 글이기는 한데 재미있었다.
    http://blog.naver.com/andwise/220920969200

    2017년 1월 22일 일요일

    [clojure][design pattern] 상태패턴 , 비지터 패턴


    clojure design pattern


    출처::http://clojure.or.kr/docs/clojure-and-gof-design-patterns.html#command

    3. 상태패턴

    유료/무료 사용자 차이, 잔액 계산, 유료 전환

    JAVA 
    1.두개의상태를 enum으로 만든다.
    public enum UserState {
      SUBSCRIPTION(Integer.MAX_VALUE), NO_SUBSCRIPTION(10);
    
      private int newsLimit; 
     
      UserState(int newsLimit) { this.newsLimit = newsLimit; }
      public int getNewsLimit() { return newsLimit; }
    }
    
    2. 유저를 만든다.
    public class User {
      private int money = 0;
      private UserState state = UserState.NO_SUBSCRIPTION;
      private final static int SUBSCRIPTION_COST = 30;
    
      public List newsFeed() { // enum안에 들어있는 리미트 값으로 보여준다.
        return DB.getNews(state.getNewsLimit());
      }
    
      public void pay(int money) { //돈을 넣어서 잔액을 지불하고 임계점을 넘으면 유료회원이 된다.
        this.money += money;
        if (state == UserState.NO_SUBSCRIPTION
            && this.money >= SUBSCRIPTION_COST) {
          // buy subscription
          state = UserState.SUBSCRIPTION;
          this.money -= SUBSCRIPTION_COST;
        }
      }
    }
    
    CLOJURE multimethod
     
    (defmulti news-feed :user-state)
    
    (defmethod news-feed :subscription [user]
      (db/news-feed))
    
    (defmethod news-feed :no-subscription [user]
      (take 10 (db/news-feed))) 
    ** defmulti는 무엇을 플래그로 멀티메소드를 만들 것인가. (:user-state으로 분기한다. 
    
    (def user (atom {:name "Jackie Brown"
                     :balance 0
                     :user-state :no-subscription}))
    
    (def ^:const SUBSCRIPTION_COST 30)
    
    (defn pay [user amount]
      (swap! user update-in [:balance] + amount)
      (when (and (>= (:balance @user) SUBSCRIPTION_COST)
                 (= :no-subscription (:user-state @user)))
        (swap! user assoc :user-state :subscription)
        (swap! user update-in [:balance] - SUBSCRIPTION_COST)))
    
    (news-feed @user) ;; top 10
    (pay user 10)
    (news-feed @user) ;; top 10
    (pay user 25)
    (news-feed @user) ;; all news
    

     4. 방문자 패턴 (Visitor pattern);
    public abstract class Item{ abstract void accept(Visitor v); }
    
    class Message extends Item { void accept(Visitor v) { v.visit(this);} }
    class Activity extends Item  { void accept(Visitor v) { v.visit(this);} }
    // 비지터
    public interface visitor {
      void visit(Activity a);
      void visit(Message m);
    }
    
    class PDFVisitor implements Visitor {
      public void visit(Activity a) { System.out.println("PDFExporter.export(a) - Activity");
      public void visit(Message m) { System.out.println("PDFExporter.export(a) - Message");
    }
    class ExcelVisitor implements Visitor {
      public void visit(Activity a) { System.out.println("ExcelExporter.export(a) - Activity");
      public void visit(Message m) { System.out.println("ExcelExporter.export(a) - Message");
    }
    main {
      Item i = new Message();
      Visitor v = new PDFVisitor();
      i.accept(v);
    }
    

    더블디스패치패턴이 생겨난 이유는(추측컨데) 인자 타입체크를 하지 않기 때문이다. 예를들어
    (비지터 패턴을 사용하지 않는 경우 가정 출처링크 참고)
    Item i = new Message();
    
    i.accept()를 실행하면 그것이 Activity인지 Message인지 알 필요가 없다. 왜냐하면 이놈은 타입체크가 이루어지기 때문이다. 하지만 아래 놈은 다르다.
    Format f = new PDF(); //여기서 Format은 PDF,EXCEL,GIF를 모두 다루는 놈이라 가정하자.
    i.accept(f)
    
    될거라 생각하지 말자.
    Format f는 인자로 들어와서 타입체크가 되지 않았다. (인자는 타입체크를 하지 않는다는 것이 중요하다.) 그래서 더블디스패치로 이중 타입체크를 하는 것이다. 하는 방법은 아주 단순하다.
    i.apply(f); 
    // 만약 이런 메소드를 실행하려 하는데 i는 타입체크가 되었고 f는 인자이기 때문에 타입체크가 안되있다면 이 메소드를 바로 실행하는 것이 아니다.
    f.apply(i); // 이렇게 바꿔서 f도 타입체크를 하도록 하는 것이다. 그러면 두번 타입체크가 되는 것이다.
    
    어쨋든 Visitor패턴으로 잘 만들었다.

    clojure

    (defmulti export
      (fn [item format] [(:type item) format]))
    {:type :message :content "Say what again!"}
    {:type :message, :content "Say what again!"}
    {:type :activity :content "Quoting Ezekiel 25:17"}
    {:type :activity, :content "Quoting Ezekiel 25:17"}
    
    (defmethod export [:activity :pdf] [item format]
     (println "exporter/activity->pdf" item))
    
    (defmethod export [:activity :xml] [item format]
      (println "exporter/activity->xml" item))
    (defmethod export [:message :pdf] [item format]
      (println "exporter/message->pdf" item))
    (defmethod export [:message :xml] [item format]
      (println "exporter/message->xml" item))
    
    (defmethod export :default [item format]
      (throw (IllegalArgumentException. "not supported")))
    
    ===============================================
    (export {:type :message } :xml)
    exporter/message->xml {:type :message}
    nil
    user=> (export {:type :message } :pdf)
    exporter/message->pdf {:type :message}
    
    ===============================================
    (하지만 :pdf:xml 사이에는 아무런 상하 관계(hierarchy)가 존재하지 않네요. 단순히 키워드일 뿐이잖아요?)
    hierarchy만들기
    (derive ::pdf ::format)
    (derive ::xml ::format)
    (isa? ::xml ::format)
    (isa? ::pdf ::format)
    
    (defmethod export [:message ::pdf] [item format] (println ":message ::pdf" item))
    (defmethod export [:message ::format] [item format] (println ":message ::FORMAT!!!!" item))
    
    (export {:type :message :content "SAYHIHIHIHI"} ::pdf)
    :message ::pdf {:type :message, :content SAYHIHIHIHI}
    nil
    user=> (export {:type :message :content "???????????"} ::xml)
    :message ::FORMAT!!!! {:type :message, :content ???????????}
    nil
    

    ::xml을 구현하지도 않았는데 만들어졌다. FORMAT으로 거슬러 올라가서 일이 실행된것!!

    ** 더블디스페치를 지원하면 비지터패턴은 의미가 없다.




    [clojure][design pattern] 클로저 디자인패턴 - 전략패턴

    clojure design pattern
    출처::http://clojure.or.kr/docs/clojure-and-gof-design-patterns.html#command

    2. 전략패턴(Strategy Pattern)

    이름순으로 정렬(단, 유료고객은 먼저)

    (def users [{:subscription true :name "Y"} {:subscription true :name "Z"} {:subscription false :name "Z"}])
    
    (sort (comparator (fn [u1 u2]
                        (cond
                          (= (:subscription u1) (:subscription u2))
                          (neg? (compare (:name u1) (:name u2)))
    
                          (:subscription u1)
                          true
    
                          :else
                          false)))
          users)
    
    위 소스는 comparator를 만들고 하는 것이 아주 비슷하다.
    아래 소스를 봐보자.
    (sort-by (juxt (complement :subscription) :name) users) ;; forward sort
    (sort-by (juxt :subscription :name) #(compare %2 %1) users) ;; reverse sort
    
    워워 하나씩 보자.
    (sort-by count ["aaa" "bb" "c"])
    
    ("c" "bb" "aaa")
    
    juxt 이녀석은 뭘까? (juxtaposition : 병렬, 병치)
    ((juxt a b c) x) == [(a x) (b x) (c x)]
    (for [user users]
      ((juxt :subscription :name) user))
    ;; ([true "Y"] [true "Z"] [false "Z"])
    
    
    (for [user users]
      ((juxt (complement :subscription) :name) user))
    ;; ([false "Y"] [false "Z"] [true "Z"])
    

    complement (보수 말하는건가) : 함수를 받아서 true/false의 반대값을 내보냄
    (def odd?? (complement even?))
    (odd?? 3)
    true 
    
    이걸 먼저 알아보자
    (sort-by (juxt (complement :subscription) :name) users) ;; forward sort
    user=> ((complement :subscription ) {:subscription "HI"})
    false
    user=> ((complement :a ) {:b "HI"})
    true
    

    두 줄로 만들어지는 것이 신기하다. juxt는 엄청난 함수 인 것 같다. 나중에 쓸일이 많을 것 같다.

    컴파일 00

    컴파일 과정
    1. 구문 해석
    2. 의미 해석
    3. 중간 표현의 생성
    4. 코드 생성

    1. 구문 해석
    소스 코드의 해석을 파스(Parse) 또는 구문해석(syntax analyzing)이라고 한다. 또한 소스 코드를 파스하는 프로그램 모듈을 파서(parser) 혹은 구문 해석시(syntax analyzer)라 한다. 그러면'컴퓨터가 이해하기 쉬운 형식'은 어떤 것일까. 그것은 구문 트리(syntax tree)라고 하는 형식이다. 구문 트리는 이름 그대로 트리 구조로 소스 코드의 문법 구조를 그대로 옮겨 놓은 구조이다.
    2. 의미 해석
    소스 코드를 Parse해서 구문 트리가 만들어졌으면, 다음에는 구문 트리를 해석해서 필요 없는 부분을 없애기도 하고 정보를 추가하기도 하여 추상 구문 트리(AST, abstract syntax tree) 데이터 구조를 만든다. 이 처리를 의미 해석(semantic analysis)이라 한다.
    - 어떤 변수가 로컬 변수인지 글로번 변수인지 구별하기
    - 변수 선언과 참조를 연결 짓기
    - 변수나 식의 타입을 체크하기
    - 변수를 참조하기 전에 초기화하고 있는지 조사하기
    - 리턴 값이 있는 함수가 값을 제대로 반환하고 있는 체크하기
    3. 중간 표현 생성
    추상 구문 트리가 생기면, 다음에는 컴파일러 내부에서만 사용되는 중간 표현(IR, intermediate representation)구조로 추상 구문 트리를 변환한다. 일부러 중간 표현을 생성하는 가장 큰 이유는 여러 종류의 프로그래밍 언어나 기계어에 대응하기 위해서다.
    예를들어 CPU에 따라 다른 컴파일러가 필요하다. (중간 표현이 없다면) 그러니 상황에 따라 모든 컴파일러를 만들어야 한다면 엄청난 수의 컴파일러가 필요하다. 그러니 만약 모든 프로그래밍 언어가 공통의 중간 표현으로 변환하면 1개의 언어, 1개의 CPU에 대응하는 코드는 각각 1개만 있으면 된다. 그래서 여러 종류의 언어나 CPU에 대응하는 컴파일러는 중간 표현을 사용하는 것이 좋다. 예를 들면 GCC는 RTL(register transfer language)이라는 중간표현을 사용한다.
    4. 코드 생성
    마지막으로 중간 표현을 어셈블리 언어로 변환한다. 이 단계를 코드 생성(code generation)이라고 한다. 코드를 생성하는 프고그램 모듈이 코드 제너레이터(code generator)이다. 코드 생성 포인트는 프로그래밍 언어와 어셈블리 언어의 차이를 얼마나 줄일 수 있는가 하는 점이다.
    5. 최적화
    실제 컴파일러에는 코드를 최적화(optimization)하는 단계까 포함된다.

    [clojure]nil 체크

    nil 과 false를 제외한 모든 것이 반복문이나 조건문에서 true가 된다.

    user=> (if nil true false)
    false
    user=> (if false true false)
    false
    user=> (if -1 true false)
    true
    user=> (if 0 true false)
    true
    user=> (if "false" true false)
    true
    하지만 비어있는 컬렉션컬렉션은?
    user=> (if [] true false)
    true
    이럴수가. 하지만 이렇게 하면 된다.
    user=> (if (not (empty? [])) true false)
    false
    이게 뭔고생인가. Joy Of Clojure에서는 다른 방법을 제시한다. 바로 (seq s)이다.
    user=> (defn print-seq [s]
      #_=>   (when (seq s)
      #_=>     (prn (first s))
      #_=>     (recur (rest s))))
    #'user/print-seq
    user=> (print-seq [])
    nil

    여기서는 rest를 쓰고 next를 쓰지 말라고 하는데 왜인지 한번 보자.
    user=> (defn my-seq [s]
      #_=>   (when (seq s)
      #_=>     (prn (first s))
      #_=>     (recur (next s))))
    #'user/my-seq
    user=> (my-seq [])
    nil

    음... 이게 문제는 아닌 것같다.
    user=> (next [])
    nil
    user=> (rest [])
    ()
    이게 문제인 것 같다.
    rest는 시퀀스가 비어있는지 아니던지 nil를 리턴하지는 않는다.
    next는 바로 nil을 리턴한다.
    우리는 각 반복 시점마다 (seq s)를 쓴다 그러니 시퀀스를 다루는 rest를 넣는게 더 조합이 잘되있다고 보는 것이다. (nil이 나오지 않게 하려 하는 점은 좋은 생각이다.)

    ** 여담으로 컬렉션을 순회할 때는 재귀보다는 doseq를 사용하는 편이낫다고 한다. 다만 doseq를 사용하면 위철머 살펴 보는 것은 힘들다. 클로저에서 do로 시작하는 구문 (doseq, dotimes, do 등)은 부수 효과를 일으키기 위해 사용하기 때문에 보통 결과로 nil을 리턴한다.

    ORA-01722: 수치가 부적합합니다 [해결]

    ORA-01722: 수치가 부적합합니다
    01722. 00000 -  "invalid number"
    *Cause:    The specified number was invalid.
    *Action:   Specify a valid number.

    이 에러는 딱 봐도 숫자가 문제가 있다는 에러인데, 확인하고 고쳐달라는 부탁(?) 오더(?)를 받았다.
    그런데 아무리 봐도 뭐가 문제 인지 알 수가 없었다. 왜냐하면

    //가상의 테이블
    ======================================
    SELECT EMAIL, MOBILE, MEMBER_NO FROM INFO Where EMAIL is not null AND USER_CODE=00000000 AND MOD(USER_NO,3)=1;
    ======================================
    이건 잘 나오는데
    ======================================
    SELECT count(*) FROM ( SELECT EMAIL, MOBILE, MEMBER_NO FROM INFO Where EMAIL is not null AND USER_CODE=00000000 AND MOD(USER_NO,3)=1 )
    ======================================
    이건 안되는 것이다.

    혹시해서
    DESCRIBE를 이용해서 테이블을 확인했다. USER_NO가 VARCHAR임을 확인하고
    TO_NUMBER를 한번 붙여보기로 했다.
    ======================================
    SELECT count(*) FROM ( SELECT EMAIL, MOBILE, MEMBER_NO FROM INFO Where EMAIL is not null AND USER_CODE=00000000 AND MOD(TO_NUMBER(USER_NO),3)=1 )
    ======================================
    그래도 안된다
    혹시해서 한 번더!
    ======================================
     SELECT count(*) FROM ( SELECT EMAIL, MOBILE, MEMBER_NO FROM INFO Where EMAIL is not null AND USER_CODE=00000000 AND TO_NUMBER(MOD(TO_NUMBER(USER_NO),3)) =1 )
    ======================================
    된다. 오라클에 대해 잘 몰라서 어떤 일이 일어났는지는 잘 모르겠다. VARCHAR에서 TO_NUMBER 했으면 숫자가 된 것이 아닌가? 그러면 count(*)는 안되고 전부 찍는 건 되는 거지? 좀 더 알아봐야할 것 같다.

    clojure 파일 쓰기 (lazy sequence를 만난 순간)

    100만개의 가짜 이메일을 만들어야 할 일이 생겼따.
    그래서 숫자를 이용해서
    0@test.com
    1@test.com
    2@test.com
    3@test.com
    4@test.com
    .
    .
    .
    1000000@test.com
    
    이렇게 만드는 것인데 클로저로 하니까 아무리 해도 안되서, 결국 자바로 만들었다.
    나는 정말 많이 부족한가보다... 라고 생각하고 있던 찰나
    일이 끝나고 저녁 먹으면서 한번 REPL을 켜서 테스트해봤다.

    (spit "/Users/tom/a.txt" 
      (clojure.string/join "\n" 
        (flatten (for [x (range 0 1000000)] 
                    (conj '() (str x "@test.com"))))))
    
    잘된다.
    클로저를 하지 말아야 하나...

    해당내용을 구글 클로저 그룹스에 물어봤는데 다들 멋지게 풀어 선보이기 시작했다.

    (spit "a.txt" (clojure.string/join "\n" 
                    (for [x (range 1000000)]
    
                      (str x "@test.com, " x))))
    
    (spit "a.csv" (with-out-str
                    (dotimes [n 1e6]
                      (println (str n "...@test.com, " n))))) 
    
    거기서 나온 조언은 lazy시퀀스가 반환될 때, dall함수나 dorun함수를 이용해서 강제로 실행시켜야 한다는 것이다.
    ex)

    (def foo (map println [1 2 3]))
    #'user/foo
    (def foo (doall (map println [1 2 3])))
    1
    2
    3
    #'user/foo
    
    lazy sequence에 대한 개념이 부족했었다. 정말... 아직 멀었다.

    [clojure][macro] 매크로사용하기 00

    Code is Data 라는 말은 코드 자체가 프로그래밍 언어의 자료구조가 된다는 말이다. 클로저 매타프로그래밍(macro)를 하게 되면 우리는 expression level에서 생각하게 된다 rather than at the textual level.

    코드 자체가 데이타(자료구조)이기 때문에 이것을 맘대로 바꿀 수 있다.

    어쨋든 이게 뭐라는 거야
    제대로 알아보자. (REPL을 예로들자)

    REPL : Read-Eval-Print loop

    Read : 문자단위표시방식 (character-based representation, typically an input stream) 을 받아서 클로저 자료구조로 변환한다. 그래서 Read phase(읽기상태)의 아웃풋은 data(데이타)이다. 이 데이터는 두번째 상태에서 평가된다. (데이터로!)

    EVEL : 어떻게 EVAL에서 code-as-data에서 data-as-code로 변할까 (자료구조안에 있는 코드들이 코드로 만들어진 데이터)

    read는 표현식(매크로가 잘 작동하는)을 작성 할 때(building the expression) 편한다. 다음의 인풋과 아웃풋을 보자. (어떻게 이게 동작하는지 잘보자.)

    첫 매크로는 바로 머리에 때려박아주기 적절한 예시가 될것(ll give you enough to dive headfirst into your first macro.)

    실제 클로저의 read 함수는 stream을 사용한다.(consume stream) 이런 행위는 셋업하는데 많은 짓을 해야 할지도 모른다.(verbose to set up.)

    이 예제의 목적에 따라. read의 형제격인 read-string을 받겠다. (stream 대신 strings를 받는 것) 
     =================================================
    (read-string "(+ 1 2 3 4 5)") 
    ;==> (+ 1 2 3 4 5)
    (class (read-string "(+ 1 2 3 4 5)"))
    ;=> clojure.lang.PersistentList

     =================================================

    여기 이 리스트가 우리가 지금까지 이야기 했던 데이터의 조각이다.
    이건 클로저 리스트다. 그리고 아직까지는 클로저 코드다.  (Code as Data)
    readstring에서 돌아온 값은 평가될 준비가 되어있다.
    이 리스트를 평가할 때, 그 표현식의 값을 알 수 있을 것이라 기대한다.

    =================================================
    (eval (read-string "(+ 1 2 3 4 5)"))
    ;=> 15
    (class (eval (read-string "(+ 1 2 3 4 5)")))
    ;=> java.lang.Long
    (+ 1 2 3 4 5)
    ;=> 15
    =================================================
    이게 REPL에서 실제로 일어나고 있는 일이다. 아니면 완전체 클로저(full-blown clojure project)를 실행할때에도 실제 일어난다. 코드는 자료구조(data structure)로 읽어지고(be read into, 저장되고) 그러고 나서 평가된다(eval). 이 두 스텝(read, evaluate)을 놓고 보았을 때,
    read와 eval 사이에 코드자신이 평가되기 위해 자기자신을 끼워넣는 것(꼽사리로 들어가는 것)을 어렵지 않을 것이다.


    예를들어 we could replace addition with multiplication:
    =================================================


    (let [expression (read-string "(+ 1 2 3 4 5)")
      (cons (read-string "*")  ;; 꼽사리로 들어가는 현장
            (rest expression)))
    ;=> (* 1 2 3 4 5)

    (eval *1) ;; *1 holds the result of the previous REPL evaluation
    ;=> 120
    =================================================
    이게 무슨 뜻이나면 read-string에다가 일단 곱하기(*)를 넣고 그 다음에는 더하기(+)를 제외한 값(rest)를 가져와서 합친것이다.

    물론, 만약 새롱누 리스트를 평가(eval)한다면, 원래 결과와는 전혀 다른 내용이 나올 것이다. 이전에 나온 예시도 나쁘지 않지만 단순히 문자열값을 읽는 것보다는 제대로 표현식을 지어보자.
    냅다 이런 "(+ 1 2 3 4 5)" 스트링을 만드는 것은 이상하단 말이야.
    하지만 이런 형태의 리스트를 생성하고 싶다면 단순히 (+ 1 2 3 4 5) 이렇게 타입한다고 될 일이 아니다. 왜냐하면 이것 실제로 표현식으로 평가될 것이기 때문이다.
    =================================================
    (let [expression (+ 1 2 3 4 5)] ;; expression is bound to 15
      (cons
        (read-string "*") ;; *
          (rest expression))) ;; (rest 15)
    ; IllegalArgumentException Don't know how to create ISeq from: java.lang.Long
    ; clojure.lang.RT.seqFrom (RT.java:505)
    =================================================
    보아라 (+ 1 2 3 4 5)가 바로 그냥 15로 평가 되는 것이다. 왜냐하면 (실제로 표현식으로 평가되기 때문에!)

    그래서 다른 솔루션이 필요하다. 하나는 실행을 억제하는 거다. 운좋게도 클로저에는 quote라는 동사(verb, 이 verb에 유념하라 function이 아니다)가 똑같은 일을 한다.
    =================================================
    (let [expression (quote (+ 1 2 3 4 5))]
      (cons (quote *)
            (rest expression)))
    ;=> (* 1 2 3 4 5)

    (eval (let [expression (quote (+ 1 2 3 4 5))]
      (cons (quote *)
            (rest expression))))
    ;= 120
    =================================================

    quote verb는 사실 클로저의 특별한 형태(Clojure special form)이며 함수가 아니다.  우리는 이 verbs를 (공식적인 의미는 아님) 함수,매크로 그리고 special forms의 결합체라고 생각하면 된다.

    리스트(Lists) 또한 verb 위치로 보여질 수 있는데 평가(eval)될 때는 함수위치로 축소되어야 한다.

    함수(function)만이 아규먼트(여기서 아규먼트란 평가될 모~든 데이터를 말하는 듯 하다)를 제대로 평가한다. (컨트롤흐름을 코드(구현한 verbs)로 패스하기전에)
      * Only functions uniformly evaluate their arguments before passing control to the code that implements the verb.


    =================================================
    (defn print-with-asterisks [printable-argument]
      (print "*****")
      (print printable-argument)
      (println "*****"))

    (print-with-asterisks "hi")
    ; *****hi*****
    ;= nil
    =================================================
    verb - 어떻게 정의하나? - 언제 아규먼트가 평가되나?
    Functions - defn - Before executing the body
    Macro -defmacro - Depends on macro; possibly multiple times or never.
    Special form - We can’t! They’re only defined by the language. - Depends on special form; possibly multiple times or never.

    자 위에 나온 print-with-asterisks같은 걸 만들 때는 굳이 언제 아규먼트가 평가(timing of the argument evaluation)되는지 알필요가 없다.

    하지만 만약! 표현식을 아규먼트로 쓴다면! 그 해당 함수(print-with-asterisks)가 평가되기 전에 평가된다. (???)
     =================================================
    (print-with-asterisks
      (do (println "in argument expression")
      "hi"))
    =================================================
    잘보라 사용하기 전에 이미 김이 다 빠져나간거다.
    Macros and speical forms, 는 이 룰에서 자유롭다. 그리고 어떤 순서에서 정의한 거든 아규먼트를 평가할 것이다. (아니면 이상하게 작동해서 망하던가...)  이게 함수와 매크로의 큰 차이란다.

    quote expression (quote ...)안에 있는 코드는 평가되지 않는다. 그리고 아주 단순한 토큰들도 가능하다. (심볼, 리스트, 벡터.. 모두)
    =================================================
    (quote 1)
    ;= 1
    (quote "hello")
    ;= "hello"
    (quote :kthx)
    ;= :kthx
    (quote kthx)
    ;= kthx
    =================================================
    근데 귀찮게 항상 QUOTE을써야 하나? 아니다 더 좋은게 있다. 
    Reader macro라는 것이다. 이건 우리가 만들 수 있는게 아니다. 언어딴에서 만들어진거라 한다.
    =================================================

    '(+ 1 2 3 4 5)
    ;= (+ 1 2 3 4 5)
    'map
    ;= map
    user.core=> map
    #object[clojure.core$map 0x589a6fa "clojure.core$map@589a6fa"]
    user.core= 'map
    map
    =================================================
    오... 확실히 먼가 다르다.
    =================================================
    (let [expression '(+ 1 2 3 4 5)]
      (cons '* (rest expression)))
    ;= (* 1 2 3 4 5)
    =================================================