태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.

'Daily life/Hard study'에 해당되는 글 38건

  1. 2012.12.07 라즈베리파이에 웹캠 연결해서 비디오 덤프 (4)
  2. 2012.08.02 도깨비: 요청/응답 json 포맷 사용 + mongodb 저장소 붙이기 (6)
  3. 2012.07.26 MongoDB 읽고쓰기 (6)
  4. 2012.07.12 도깨비(tokebi) 더미 API (5)
  5. 2012.07.05 2012/07/05: Hello world in xcode (5)
  6. 2011.09.03 An introduction into SICP (6)
  7. 2011.07.13 Reading: How To Design Programs(HTDP)
  8. 2011.05.12 D 프로그래밍 언어 - 오타(?) (3)
  9. 2011.04.27 SICP study: 근황 (3)
  10. 2010.10.21 예술의 전당 수묵화반, 전시회 준비 (3)
  11. 2010.08.05 지름: Prog. Clojure - 허접한 이북리더지만 쓸모는 있다 (8)
  12. 2010.06.28 소나무 연습
  13. 2010.04.16 숙제를 하고 싶었는데...
  14. 2010.03.21 처음 그려본 소나무 (7)
  15. 2010.03.19 숙제, 그리고 그 후 ...
  16. 2010.03.19 부랴부랴 숙제중
  17. 2009.12.22 디군이 가장 많이 산 책: 전도서에 바치는 장미 (2)
  18. 2009.12.05 안드로메다 #1 (2)
  19. 2009.01.09 코딩도장: 회고 (1)
  20. 2008.10.26 후기: SICP 스터디모임 10월 정모 (4)
  21. 2008.09.30 SICP 스터디모임 9월 정모 늦은 후기 (6)
  22. 2008.08.03 Firefox plugin: 분홍이+Scrapbook >_<=b (2)
  23. 2008.07.29 SICP 스터디 7월 정모 (5)
  24. 2008.07.19 지름: GEB
  25. 2008.06.16 후기: 코딩도장 오프모임 (2)
  26. 2008.06.15 후기: SICP 스터디 모임 6월(+5월) 정모 (5)
  27. 2008.04.26 SICP 스터디 모임 4월 정기모임 (3)
  28. 2008.03.27 SICP 스터디 모임 늦은 후기 (4)
  29. 2008.02.23 지름: SICP (8)
  30. 2008.02.17 지름: Programming Erlang(pdf) (2)

라즈베리파이에 웹캠 연결해서 비디오 덤프

Daily life/Hard study 2012.12.07 23:22

# 목표: 동영상을 캡쳐한다

# 들어가기 전에
    * opencv 라이브러리 설치: sudo apt-get install libopencv-*
    * pyopencv 설치: sudo pip install pyopencv
    * Doc: http://docs.opencv.org/

    * OpenCV 튜토리얼: http://docs.opencv.org/doc/tutorials/tutorials.html
    * OpenCV Python: http://opencv.willowgarage.com/wiki/PythonInterface
    튜토리얼이 많이 부족하다. 아직 작업중인데, 당신이 쓸거면 연락달라고 쓰인걸로 봐서 한동안은 기대할 수 없을듯.


# Modules:
     * core
     * imgproc
     * video
     * calib3d
     * features2d
     * objdetect
     * highgui
     * gpu
     * and so on …


# API Concepts

    * 모든 클래스와 함수는 cv 이름공간 안에 있다.
    * OpenCV 는 메모리 해제를 스스로 한다. 레퍼런스 카운트 시스템이 있다.
    * 대부분의 경우 함수의 결과물에 대한 메모리 할당도 스스로 한다.
    * 라이브러리 전체적으로 Saturation arithmetic 사용
    * 템플릿은 좀 자제했음 - 파이썬, 자바 등 다른 언어로 바인딩을 쉽게하고 인터페이스/구현 분리를 잘 하기 위해
    * 편의성 및 중복 제거를 위해 InputArray, OutputArray 가 여기저기서 사용됨
    * OpenCV 는 예외를 사용한다 (derived from cv::Exception)
    * fully re-enterable


# How to capture video?

내가 필요한건 highgui(http://docs.opencv.org/modules/highgui/doc/highgui.html) 에 있는 것 같다.
highgui 문서 앞쪽에 보면 아래와 같은 설명이 있다.

   * Create and manipulate windows that can display images and “remember” their content (no need to handle repaint events from OS).
   * Add trackbars to the windows, handle simple mouse events as well as keyboard commands.
   * Read and write images to/from disk or memory.
   * Read video from camera or file and write video to a file.


cv::VideoCapture 클래스가 있다. 그리고 API 페이지(http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html#videocapture)를 보니, python, C 인터페이스까지 나와 있다. C/C++ 인터페이스가 별도라는 데 주의. 일단 C++ 로 먼저 해본다.

VideoCapture 클래스로 웹캠에서 프레임을 얻어내고, 이렇게 얻어낸 프레임들을 imwrite 로 이미지파일로 각각 저장하거나 VideoWriter 을 사용해서 영상파일로 덤프하자.


# C++ 코드 컴파일

    * /usr/include/opencv, /usr/include/opencv2 이렇게 디렉토리가 두개다. opencv 는 이전 인터페이스를 하위호환성때문에 유지하는 듯. opencv2/opencv.hpp 를 가져다 쓰자.
    * 링크할 때 -lopencv_core -lopencv_highgui 이렇게 core 와 사용할(한) 모듈을 주어야 한다. -lopencv 이 아니다.


# Code

#include <cstdio>
#include <boost/format.hpp>
#include <opencv2/opencv.hpp>

int main(int, char**) {
    cv::VideoCapture cap(0);
    if (!cap.isOpened()) return -1;

    cv::Mat frame;
    for (int i=0; i<10; ++i) {
        cv::Mat image;
        while (!cap.read(image))
            usleep(1000*100);
        std::fprintf(stderr, "%d\n", i);
        cv::imwrite((boost::format("%05d.jpg") % i).str(), image);
    }
    return 0;
}

근데 망했다. 파일이 나오긴 나오지만 다 그냥 까만 화면이다. 카메라에 설정값이 뭔가 안좋은듯 하다. BRIGHTNESS, CONTRAST, SATURATION, GAIN 를 모두 적당한 값(중간값)으로 세팅해봐야겠다. 일단 지금 값을 확인하기 위한 함수를 하나 만들고 스케일에 맞춰 적당한 값을 넣자.



0~255 스케일이라고 생각했는데 0.0~1.0 스케일이다. 0.5 를 넣는다.

#include <cstdio>
#include <boost/format.hpp>
#include <opencv2/opencv.hpp>

void printProperties(cv::VideoCapture& cap) {
    fprintf(stdout, "CV_CAP_PROP_POS_MSEC    : %f\n", cap.get(CV_CAP_PROP_POS_MSEC));
    fprintf(stdout, "CV_CAP_PROP_FRAME_WIDTH : %f\n", cap.get(CV_CAP_PROP_FRAME_WIDTH));
    fprintf(stdout, "CV_CAP_PROP_FRAME_HEIGHT: %f\n", cap.get(CV_CAP_PROP_FRAME_HEIGHT));
    fprintf(stdout, "CV_CAP_PROP_FPS         : %f\n", cap.get(CV_CAP_PROP_FPS));
    fprintf(stdout, "CV_CAP_PROP_MODE        : %f\n", cap.get(CV_CAP_PROP_MODE));
    fprintf(stdout, "CV_CAP_PROP_BRIGHTNESS  : %f\n", cap.get(CV_CAP_PROP_BRIGHTNESS));
    fprintf(stdout, "CV_CAP_PROP_CONTRAST    : %f\n", cap.get(CV_CAP_PROP_CONTRAST));
    fprintf(stdout, "CV_CAP_PROP_SATURATION  : %f\n", cap.get(CV_CAP_PROP_SATURATION));
    fprintf(stdout, "CV_CAP_PROP_GAIN        : %f\n", cap.get(CV_CAP_PROP_GAIN));
    fprintf(stdout, "CV_CAP_PROP_HUE         : %f\n\n", cap.get(CV_CAP_PROP_HUE));
}

int main(int, char**) {
    cv::VideoCapture cap(0);
    if (!cap.isOpened()) return -1;

    printProperties(cap);
    cap.set(CV_CAP_PROP_BRIGHTNESS, 0.5);
    cap.set(CV_CAP_PROP_CONTRAST, 0.5);
    cap.set(CV_CAP_PROP_SATURATION, 0.5);
    cap.set(CV_CAP_PROP_GAIN, 0.5);
    cap.set(CV_CAP_PROP_FRAME_WIDTH, 160);
    cap.set(CV_CAP_PROP_FRAME_HEIGHT, 120);
    printProperties(cap);

    cv::Mat frame;
    for (int i=0; i<10; ++i) {
        cv::Mat image;
        while (!cap.read(image))
            usleep(1000*100);
        std::fprintf(stderr, "%f\n", i);
        cv::imwrite((boost::format("%05d.jpg") % i).str(), image);
    }
    return 0;
}




select timeout 이 뜨면서 계속 까만 화면만 나온다. 구글링 결과 http://www.raspberrypi.org/phpBB3/viewtopic.php?f=37&t=11745 이런 페이지를 발견했다! 그리고 원래 잘 동작하던 uvccapture 로도 까만 스샷만 나오고 있다. 파이를 리붓해보자.



현재 라즈베리파이와 웹캠을 던져둔 방에 불이 꺼져서 어두운 상태라는 것이 확인되었다 -_-; 집으로 컴백 후 장소를 옮기고 나서 작업해야겠다. ㅠㅠ


Trackbacks 0 : Comments 4

도깨비: 요청/응답 json 포맷 사용 + mongodb 저장소 붙이기

Daily life/Hard study 2012.08.02 22:20

지난번에 작업 - http://blog.dgoon.net/449 - 하다 말았던 부분을 이어서 한다. 도깨비에 추가로 할 작업은 다음 두가지이다.

1. 일단 요청/응답 포맷을 json으로 바꾼다.
2. persistent layer를 붙인다(mongodb?)

-----
query string 이나 parameter 는 버린다.
POST 로 요청을 받고 -> Content-type 이 application/json 인지 확인하고 -> body 를 읽어서 json.loads 를 한다.
요청이 가져야 하는 값들은 여기서 나오는 json 에 모두 포함되어 있어야 한다. 몇 가지 사소한 것들을 적어둔다.

a. 요청 타입(POST/GET)은 지난번에 @require_http_methods("POST") 로 강제했다
b. content-type은 request.META.get('CONTENT-TYPE') 에 들어있다.
c. body는 request.read() 를 하면 나온다

필수 데이터가 들어있는지 확인하는 코드(check_required)와 content-type을 확인하는 코드(check-content-type)을 함수로 분리하면 더미 로직은 get/put 둘 모두,

        check_content_type(request, "application/json")
        body = request.read()
        req_dict = simplejson.loads(body)
        check_required(GET_REQUIRED_FIELDS, req_dict.keys())
        print req_dict


이 정도로 간단해진다. 조건이 맞지 않는 경우 check_required, check_content_type 함수들은 직접 예외를 생성해서 던진다. 테스트 요청을 날리던 get.py, put.py 에서는 content-type 만 application/json 으로 바꾸고, urlencode 하던 부분을 simplejson.dumps 로만 바꾸면 된다.

{'app_key': 'deadbeef', 'condition': {}}
[02/Aug/2012 21:06:05] "POST /api/v1/get/ HTTP/1.1" 200 2
{'content': 'THIS_IS_CONTENT', 'tag_list': ['tag1', 'tag2', 'tag3'], 'meta': {}, 'app_key': 'deadbeef'}
[02/Aug/2012 21:06:16] "POST /api/v1/put/ HTTP/1.1" 200 2

이전보다 코드도 짧아지고, tag_list 도 리스트로 제대로 들어온다. 에잉 훨 낫네.

-----
persistent layer를 붙인다. 전에 튜토리얼을 따라가본 몽고디비를 쓰고 싶다. 그때는 c++ 클라이언트였는데, 파이썬이라고 뭐 딱히 다르진 않지 싶다. 더 쉽겠지...

http://www.mongodb.org/display/DOCS/Drivers -> http://www.mongodb.org/display/DOCS/Python+Language+Center

여기서 찾아보면 몇 가지 라이브러리가 있다. PyMongo 를 선택. 일단 언제나처럼 pip,easy_install,apt-get 순서로 패키지가 있나 뒤져본다.

sudo pip install pymongo

빙고. 파이썬 쉘 띄워서 import pymongo 해보니 뭔가 임포트 된다. 이걸 쓰면 되겠다.

http://api.mongodb.org/python/current/tutorial.html

여기 튜토리얼이니 잠깐 읽어보면 어렵지 않아요~ 지난번에 삽질하면서 katy 에 몽고디비 설치는 해 두었으니 그대로 활용하자. db=tokebi, collection=twit 으로 간다. store_mongodb.py 를 만들고, MongoStore 클래스에 몽고디비 접근 구현 코드를 둔다. 인터페이스는 mongo_store.get/put 이며 api_get, api_put 이 받은 요청을 거의 그대로 받는다.


api_get:
        app_key, condition = get_items(req_dict, GET_REQUIRED_FIELDS)             
        store = MongoStore('twit')
        retrieved = store.get(app_key, condition)

api_put:
        app_key, content, tag_list, meta = get_items(req_dict, PUT_REQUIRED_FIELDS)
        store = MongoStore('twit')
        ack = store.put(app_key, content, tag_list, meta)

api_get, api_put 에 각각 위의 코드가 추가되었다. 테스트 요청을 날려보면서 디버깅을 좀 해야 했는데, python get.py localhost 2223 > ~/public_html/get_error.html 등으로 저장한 뒤에 브라우저로 열어보면 좀 쉽다. 몽고디비에 쓰고 읽을 때 필요한 자료구조는 뭘까 하고 튜토리얼을 봤더니,

In PyMongo we use dictionaries to represent documents. ...

저장 단위가 파이썬 맵이므로 구현은 아주 간단하다. 심지어 문서를 꺼내오는 조건도 사전이므로,몽고디비 접근은 거의 자명하다.

#-*- encoding: utf-8 -*-from pymongo import Connection
CONN = Connection()

def doc2dict(d):
    ret = dict(d)
    ret['_id'] = str(ret['_id'])
    return ret

class MongoStore(object):
    def __init__(self, collection):
        self.collection = CONN.tokebi[collection]

    def get(self, app_key, condition):
        cond = condition.copy()
        cond['app_key'] = app_key
        return map(doc2dict, self.collection.find(condition))

    def put(self, app_key, content, tag_list, meta):
        doc = {
            'app_key': app_key,
            'content': content,
            'tag_list': tag_list,
            'meta': meta,
        }
        return self.collection.insert(doc)


api_get, api_put 에서 응답을 적절히 json 포맷으로 만들어 준다면 테스트 요청을 날리는 get.py 에서는 여러개의 문서 리스트를 응답으로 얻게 된다. 받은 결과를 화면에 찍어보면,

dgoon@katy:~/works/tokebi/sample_query$ python get.py localhost 2223
{'Content-type': 'application/json', 'Accept': 'text/plain'}
{'condition': {}, 'app_key': 'deadbeef'}
0
        content THIS_IS_CONTENT
        _id 501a74c272653e2217c9b255
        meta {}
        app_key deadbeef
        tag_list ['tag1', 'tag2', 'tag3']
1
        content THIS_IS_CONTENT
        _id 501a74d472653e222068f2b8
        meta {}
        app_key deadbeef
        tag_list ['tag1', 'tag2', 'tag3']
2
        content 두만강푸른물에
        _id 501a750372653e222068f2b9
        meta {}
        app_key deadbeef
        tag_list [u'\uac15\uc0b0\uc5d0', u'\ub178\ub798']
3
        content 티비유치원 하나둘셋
        _id 501a768072653e225e6cb3c1
        meta {}
        app_key deadbeef
        tag_list ['hana', 'dul', 'set']


이렇게 확인할 수 있다. 한글도 잘 들어가는 것을 확인할 수 있다. 2번 문서의 tag_list에 들어있는 것은,

>>> for k in [u'\uac15\uc0b0\uc5d0', u'\ub178\ub798']:
...     print k
...
강산에
노래

이렇게 유효한 한글이다. PyMongo 튜토리얼에서 설명되어 있는데 - BSON strings are UTF-8 encoded 라고 한다. Unicode문자열은 encode 되어 저장된다고. 그리고 simplejson 으로 직렬화 할때는 다시 유니코드가 되어서 응답을 받은 쪽에서는 한글이 모두 유니코드로 떨어지게 된다. 나중에 좀 헷갈릴 것 같다.


-> http://git.dgoon.net/?p=tokebi.git;a=commit;h=da49398646c0812a0d33d71cbec23e5052f5ca09


-----

추가적으로 해야 하는 작업은 ... 잊고 있었는데, 나 말고 다른 사람도 이걸 쓰게 하려면


1. user_id 같은걸 넣어야 하고,


최신 엔트리를 가져오려면,


2. created_at 이 필요하고,


사람을 가리거나 정렬이 필요하니


3. 적절한 인덱스 가 필요하다.


다음 작업은 이상 3개가 되겠다.

Trackbacks 0 : Comments 6

MongoDB 읽고쓰기

Daily life/Hard study 2012.07.26 23:10

몽고DB의 C++ 클라이언트는 github 에서 구할 수 있다.

mongodb c++ client: github.com/mongodb/mongo
mongodb 서버는 빌드해서 설치할 수도 있지만 귀찮으니, apt-get 으로 설치하기로 한다.

a. debian: mongodb-server mongodb-dev mongodb-clients mongodb
b. ubuntu: mongodb
c. etc: http://docs.mongodb.org/manual/tutorial/install-mongodb-on-debian-or-ubuntu-linux/

세번째가 제일 나은 듯 하다. 그 와중에 upstart 라는걸 보게 되었다. 나중에 천천히 읽어볼 것: http://upstart.ubuntu.com/
사용한 client 라이브러리는 C++ 2.1.2 이다. API DOC: http://api.mongodb.org/cplusplus/2.1.0/


0. Install
  mongodb server:
  sudo apt-key adv --keyserver keyserver.ubuntu.com --recv 7F0CEB10
  echo "deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen" >> /etc/apt/sources.list
  sudo apt-get update
  sudo apt-get install mongodb-10gen


  mongodb client library:
  wget http://downloads.mongodb.org/cxx-driver/mongodb-linux-x86_64-2.1.2.tgz
  tar zxvf mongodb-linux-x86_64-2.1.2.tgz
  cd mongo-cxx-driver-nightly
  sudo scons install

이렇게 하면 /usr/local 밑에 몽고DB 클라이언트 라이브러리가 설치된다.
/usr/local/include/mongo 아래쪽 권한이 좀 이상하게 설치되어서 sudo chmod 755 /usr/local/include/mongo -R 을 해 주었다.
언제나 그런건지, scons 사용을 잘못한건지는 잘 모르겠다.

C++ 튜토리얼은 이걸 보면 된다. http://www.mongodb.org/pages/viewpage.action?pageId=133415
이 아래의 내용은 기본적으로 튜토리얼 내용을 그냥 따라간 로그이다.

1. Connect
#include <cstdio>
#include "client/dbclient.h"
using namespace std;

int main() {
    try {
        mongo::DBClientConnection c;
        c.connect("localhost");
    }
    catch (mongo::DBException& e) {
        printf("Error: %s\n", e.what());
    }
    return 0;
}


g++ -I/usr/local/include/mongo test.cpp -lboost_system -lboost_thread -lboost_filesystem -lmongoclient -o test

이렇게 빌드하면 ./test 를 실행했을때 아무일도 안 일어나게 된다. localhost 를 remotehost 같은걸로 바꾸고 해보면 에러가 나는걸 확인할 수 있다.


$ ./test
Thu Jul 26 21:36:19 getaddrinfo("remotehost") failed: No address associated with hostname
Error: can't connect couldn't connect to server remotehost:27017

2. Insert
접속은 했으니 이제 읽기/쓰기를 해보자. 아직 아무것도 들어있는게 없으므로, 먼저 쓰기를 하고 나서 쓴 값을 읽어보도록 하자. 몽고DB는 데이터를 BSON이라는 형식으로 저장하는데, JSON이랑 닮은 - 정확히는 json with custom extension - 포맷이다. 방법은 간단하다. BSON 객체를 생성 -> DB에 추가, 하면 된다.

a. BSON 객체 생성
BSON 객체는 BSONObjBuilder 를 사용해서 만든다. 사용법은 아래와 같다.

BSONObjBuilder builder;
builder.append("HERE_GOES_KEY", "HERE_GOES_VALUE");
builder.append("name", "dgoon");
BSONObj obj = builder.obj();

이걸 아래와 같이 체이닝 할 수도 있다

BSONObj obj = BSONObjBuilder().append("HERE_GOES_KEY", "HERE_GOES_VALUE").append("name", "dgoon").obj();

하지만 난 여러 줄에 풀어 쓰는걸 선호한다.
그 외에 stream 체이닝이나, Object ID 를 명시적으로 지정하는 방법 등 몇 가지 화두가 더 있지만 과감하게 생략.

b. DB 에 추가
이렇게 만든 BSONObj 객체를 DB에 넣기 위해서는,

conn.insert("database.collection", obj);

를 해주면 된다. database, collection 은 무엇인가! 대충 RDB 개념을 빌리자면 database=database, collection=table 정도로 생각하면 된다.

#include <cstdio>
#include "client/dbclient.h"
using namespace std;

int main() {
    try {
        mongo::DBClientConnection c;
        c.connect("localhost");

        mongo::BSONObjBuilder b;
        b.append("name","dgoon");
        b.append("addr", "nakseongdae");
        mongo::BSONObj p=b.obj();

        c.insert("mydb.character", p);
    }
    catch (mongo::DBException& e) {
        printf("Error: %s\n", e.what());
    }
    return 0;
}


소스코드를 이렇게 수정하고 나서 실행해보면, 역시 아무런 말도 없다. 잘 된거겠지… 확인을 해보자. 읽는 코드를 만들기 전에 잠깐 shell 에서 보도록 한다.

c. SHELL

몽고DB는 강력한지는 잘 모르겠고, 여튼 쉘 환경이 있다. 위의 c로 설치했다면 mongo 라는 커맨드가 있을 것이다.

dgoon@katy:~/works/mongo-cxx-driver-nightly$ mongo
MongoDB shell version: 2.0.6
connecting to: test
>

위에서 mydb.character 에 데이터를 넣었던 것이 기억나는가? db=mydb, collection=character 이다.

> show dbs
local     (empty)
mydb     0.078125GB
> use mydb
switched to db mydb
> show collections
character
coll
system.indexes
things
> db
mydb
> db.character
mydb.character
> db.character.find()
{ "_id" : ObjectId("50113dbb9aa68f690df58a39"), "name" : "dgoon", "addr" : "nakseongdae" }
{ "_id" : ObjectId("50113dbe9aa68f690df58a3a"), "name" : "dgoon", "addr" : "nakseongdae" }
{ "_id" : ObjectId("50113dbf9aa68f690df58a3b"), "name" : "dgoon", "addr" : "nakseongdae" }
>

이렇게 들어가 있다. … 왜 3개냐구? 내가 실행을 3번 했으니까. … 그렇다, 딱히 KEY 를 지정해 준 것이 아니기 때문에 여러번 추가해도 중복이 아니므로, 실행한 횟수만큼 여러개가 들어간다.


3. Retrieve

읽어오는건 코드가 조금 더 길다. 읽어올 조건과 함께 query를 날린다 -> 커서를 받는다 -> 커서를 순회하며 데이터를 꺼낸다, 순서로 작업하면 된다.

데이터를 꺼내오기 위해 명시해 주어야 하는 것은, 어디에서? 어떤 녀석을? 이다. connection.query 는 2개의 인자를 받는데, 하나는 어디에서? 또 하나는 어떤 녀석을? 에 대한 것이다. 여기서 조건 역시 BSONObj 타입으로 들어간다. 그냥 빈 객체를 넣으면 모두 다 꺼내오라는 뜻이다. BSONObj 객체의 각 필드가 AND로 조합되어 조건으로 들어간다고 한다. 우리는 모두다 꺼내와보자.

test.cpp 는 이제 아래와 같이 길어졌다.

#include <cstdio>
#include "client/dbclient.h"
using namespace std;

int main() {
    try {
        mongo::DBClientConnection c;
        c.connect("localhost");

        mongo::BSONObjBuilder b;
        b.append("name","dgoon");
        b.append("addr", "nakseongdae");
        mongo::BSONObj p=b.obj();

        c.insert("mydb.character", p);

        auto cursor=c.query("mydb.character",
                            mongo::BSONObj());
        while (cursor->more()) {
            printf("%s\n", cursor->next().toString().c_str());
        }
    }
    catch (mongo::DBException& e) {
        printf("Error: %s\n", e.what());
    }
    return 0;
}


auto는 타이핑을 줄이기 위해 사용했는데, 정확한 타입은 auto_ptr<mongo::DBClientCursor> 이다. auto_ptr을 사용한 이유는 소유권을 넘기면서 객체 소멸을 라이브러리 사용자(caller)에게 넘기고 난 귀찮으니 잊어버리겠어 -_- 라는 것이라 생각한다. auto 사용때문에 아래 컴파일 옵션에 -std=c++0x 가 추가된 것에 주의하자.

dgoon@katy:~/works/mongo-cxx-driver-nightly$ g++ -I/usr/local/include/mongo test.cpp -lboost_system -lboost_thread -lboost_filesystem -lmongoclient -std=c++0x -o test
goon@katy:~/works/mongo-cxx-driver-nightly$ ./test
{ _id: ObjectId('50113dbb9aa68f690df58a39'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('50113dbe9aa68f690df58a3a'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('50113dbf9aa68f690df58a3b'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('501140789aa68f690df58a3c'), name: "dgoon", addr: "nakseongdae" }
goon@katy:~/works/mongo-cxx-driver-nightly$ ./test
{ _id: ObjectId('50113dbb9aa68f690df58a39'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('50113dbe9aa68f690df58a3a'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('50113dbf9aa68f690df58a3b'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('501140789aa68f690df58a3c'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('501140799aa68f690df58a3d'), name: "dgoon", addr: "nakseongdae" }
goon@katy:~/works/mongo-cxx-driver-nightly$ ./test
{ _id: ObjectId('50113dbb9aa68f690df58a39'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('50113dbe9aa68f690df58a3a'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('50113dbf9aa68f690df58a3b'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('501140789aa68f690df58a3c'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('501140799aa68f690df58a3d'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('5011407a9aa68f690df58a3e'), name: "dgoon", addr: "nakseongdae" }
goon@katy:~/works/mongo-cxx-driver-nightly$ ./test
{ _id: ObjectId('50113dbb9aa68f690df58a39'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('50113dbe9aa68f690df58a3a'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('50113dbf9aa68f690df58a3b'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('501140789aa68f690df58a3c'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('501140799aa68f690df58a3d'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('5011407a9aa68f690df58a3e'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('5011407b9aa68f690df58a3f'), name: "dgoon", addr: "nakseongdae" }
goon@katy:~/works/mongo-cxx-driver-nightly$

실행할 때마다 엔트리가 하나씩 늘어나면서 가져오는 갯수도 늘어나고 있다. 참고로 b.append("age", 18); 처럼 value 에는 문자열 이외의 타입도 들어갈 수 있다.


4. Update

데이터를 가져오는 것과 별로 다르지 않다. 어디에 있는? 어떤 아이들에? 어떤 값을? 을 지정해주면 된다.


#include <cstdio>
#include "client/dbclient.h"
using namespace std;

int main() {
    try {
        mongo::DBClientConnection c;
        c.connect("localhost");

        c.update("mydb.character", // 어디에 있는?
                 BSON("age" << 18),  // 어떤 아이들에게?
                 BSON("$inc" << BSON("visits" << 1)));  // 어떤 값을??

        auto cursor=c.query("mydb.character",
                            mongo::BSONObj());
        while (cursor->more()) {
            printf("%s\n", cursor->next().toString().c_str());
        }
    }
    catch (mongo::DBException& e) {
        printf("Error: %s\n", e.what());
    }
    return 0;
}

이렇게 하면 mydb.character 에서 age=18 인 아이들을 찾아서(설명엔 없지만 위에서 몇개 추가해 두었다) visits 을 1 증가시킨다고 한다. $inc 가 뭔가 특별한 의미가 있나보다. 그래서 위의 프로그램을 여러번 실행하면,

dgoon@katy:~/works/mongo-cxx-driver-nightly$ ./test
{ _id: ObjectId('50113dbb9aa68f690df58a39'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('50113dbe9aa68f690df58a3a'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('50113dbf9aa68f690df58a3b'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('501140789aa68f690df58a3c'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('501140799aa68f690df58a3d'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('5011407a9aa68f690df58a3e'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('5011407b9aa68f690df58a3f'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('5011421d9aa68f690df58a40'), addr: "nakseongdae", age: 18, name: "dgoon", visits: 10 }
{ _id: ObjectId('5011421f9aa68f690df58a41'), addr: "nakseongdae", age: 18, name: "dgoon", visits: 1 }
dgoon@katy:~/works/mongo-cxx-driver-nightly$ ./test
{ _id: ObjectId('50113dbb9aa68f690df58a39'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('50113dbe9aa68f690df58a3a'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('50113dbf9aa68f690df58a3b'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('501140789aa68f690df58a3c'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('501140799aa68f690df58a3d'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('5011407a9aa68f690df58a3e'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('5011407b9aa68f690df58a3f'), name: "dgoon", addr: "nakseongdae" }
{ _id: ObjectId('5011421d9aa68f690df58a40'), addr: "nakseongdae", age: 18, name: "dgoon", visits: 11 }
{ _id: ObjectId('5011421f9aa68f690df58a41'), addr: "nakseongdae", age: 18, name: "dgoon", visits: 1 }

이런 결과가 나온다. 처음 한 번은 5011421f9aa68f690df58a41 를 고치더니 그 후로는 5011421d9aa68f690df58a40 를 고치고 있다. 어어?
… 요약하면 해당 조건에 맞는 데이터가 여러개 있는 경우에, 그 중 임의로 하나를 선택하여 갱신, 한다고 생각한다.
혹은 선택 기준은 있지만, 갱신할 때마다 선택되는 아이들이 달라지게 될 수도 있다. 여튼 곤란.

uniqueness 를 보장하기 위해서는 매번 업데이트 할 때마다 query를 보내어 확인을 한번 해 주어야 하는건가?

조건에 맞는 엔트리가 하나도 없는 경우는 아무일도 일어나지 않는다.



+
http://api.mongodb.org/cplusplus/2.1.0/dbclient_8h_source.html


ensureIndex의 함수 시그니쳐는 다음과 같다. unique=false가 기본값인데, true를 줄 수 있다.


virtual bool ensureIndex( const string &ns , BSONObj keys , bool unique = false, const string &name = "", bool cache = true, bool background = false, int v = -1 );


주의할 점은 특정 key에 unique=true가 걸리면 그 키가 동일한 경우는 물론 이고, 해당 키가 "없는 것도 하나의 값" 이 되어 버린다. 즉 ensureIndex("mydb.character", fromjson("{age:1}"), true); 로 인덱스를 생성했다면

mydb.character 에 age 가 없는 데이터는 단 하나밖에 존재할 수 없다

는 사실.


+ 도깨비의 백엔드 저장소로 몽고디비 사용할 예정.


Trackbacks 0 : Comments 6

도깨비(tokebi) 더미 API

Daily life/Hard study 2012.07.12 22:19


목표: 아이폰에서 읽고 쓸 수 있는 임의의 저장소 인터페이스를(만) 만드는 것


1. Initial commit
  - put: (app_key, content, tag_list, meta_info)
  - get: (app_key, condition)

  일단 일반적인 모양만 정의하고 세세한 사용은 나중에 정의하기 위하여 메타정보, 조건 등을 dictionary 로 받는걸로 한다. POST 요청만 다루며 GET은 예외를 던져버리도록 하자. 일단 인터페이스를 만드는게 목표이므로 실제로 데이터를 저장하는 부분은 고려하지 않는다.


성능은 고려사항이 아니다. 아이폰 앱을 만들 때 붙여놓을 더미가 필요한 것이므로 django로 후다닥 작성한다. django 1.4 기준이다. 혹시 나중에 필요하게 되면 비동기로 다시 작성한다. 개인적으로 eventlet 에 관심이 ...

  app_key: fixed-length binary
  content: binary
  tag_list: list of strings
  meta_info: dictionary
  condition: dictionary

  URL:
    - /api/v1/put
    - /api/v1/get

  이 저장소는 만들어질 아이폰 앱과는 완전히 별개의 코드베이스이므로 독립적인 git 저장소를 생성한다. dgoon's key value storage -> dkv -> 도깨비 -> tokebi 로 한다. 난 부끄러움이 많으니까 github에 공개 저장소를 만들진 않는다. 하지만 비공개 저장소도 싫으니까 개인서버에 조용히 판다. git.dgoon.net

django 프로젝트(tokebi)를 생성하고 기본 세팅. RDB는 사용하지 않을 예정이므로 DATABASES는 깔끔하게 날린다.
나중에 관리 페이지라던가 다른게 생길 수 있으니 api 라는 애플리케이션을 생성해서 거기에 코드를 넣는다.
위에서 정의한대로 필요한 콜은 put, get 두개이다. 이 각각을 받아줄 endpoint 를 설정한다. 정의되지 않은 URL에 대해서는 일단 404를 알아서 만들도록 둔다. secret key 따위 알게뭐야 흥.

----- api/views.py -----
from django.http import HttpResponse

def api_get(request):
    return HttpResponse('OK', status=200)

def api_put(request):
    return HttpResponse('OK', status=200)


----- tokebi/urls.py -----
...
from api.views import api_get, api_put

urlpatterns = patterns('',
    url(r'^api/v1/get/', api_get),
    url(r'^api/v1/put/', api_put),

…)


이렇게 하고 테스트 서버를 띄워서 접속하면 /api/v1/get, /api/v2/put 에 대해서 OK 를 얻을 수 있다. 일단 여기서 Initial commit 을 해 둔다.


2. Test request

필요한 정보가 모두 있는 경우 OK를, 부족한 경우 500 에러를 내도록 한다. 이게 되어야 개발에 사용할 더미로써 가치가 있다.
그러기 위해선 쿼리를 보낼 수 있어야 한다.

put.py, get.py 를 만든다.

  - put.py: 명령행에서 HOST PORT CONTENT TAG_LIST 를 입력받는다. 메타정보는 일단 {} 를 넣는다.
  - get.py: 명령행에서 HOST PORT 를 입력받는다. 조건에 {} 를 넣는다.

두 경우 모두 app_key 는 'deadbeef' 를 하드코딩한다. 요청은 파이썬으로 작성한다. httplib.HTTPConnection(http://docs.python.org/library/httplib.html#httpconnection-objects) 을 사용하면 되겠지.

----- put.py -----
import sys, httplib, urllib

APP_KEY = 'deadbeef'

if '__main__'==__name__:
    if len(sys.argv)<5:
        print('Usage: %s host port content tag_list' % sys.argv[0])
        sys.exit(0)

    host = sys.argv[1]
    port = int(sys.argv[2])
    content = sys.argv[3]
    tag_list = sys.argv[4:]

    conn = httplib.HTTPConnection(host, port)
    body = {
        'app_key': APP_KEY,
        'content': content,
        'tag_list': tag_list,
        'meta': {},
    }
    header = {
        'Content-type': 'application/x-www-form-urlencoded',
        'Accept': 'text/plain',
    }
    conn.request("POST", "/api/v1/put/", urllib.urlencode(body), header)

    res = conn.getresponse()
    res_body = res.read()
    assert(res.status==200 and res_body=='OK')


그런데 python put.py localhost 2223 HAHAHA tag1 tag2 tag3 를 실행해더니 assertion error가 떳다.

[12/Jul/2012 21:06:55] "POST /api/v1/put/ HTTP/1.1" 403 2282
[12/Jul/2012 21:07:07] "GET /api/v1/put/ HTTP/1.1" 200 2

403이 put.py 로 날린 요청, 200 이 브라우저에서 날린 요청이다. 500도 아니고 403 ?? put.py 에서 res_body 를 찍게 해 봤더니,
csrf관련된 내용이 보인다. 말인 즉슨, cross-site request forgery를 방지하기 위한 미들웨어가 있는데 그녀석을 위한 토큰이 포함되어야 한다나 어쩐다나.
나는 템플릿을 쓸 생각이 없고, 요청에 그런걸 만들어 보내는것도 귀찮으므로 csrf 관련 옵션을 비활성화 해버리겠다. settings.py 에서 csrf 로 검색하면 바로 나온다. MIDDLEWARE_CLASSES 에서 django.middleware.csrf.CsrfViewMiddleware 를 주석처리하고 나니 잘 된다.

api_put 을 고쳐서 들어온 body 를 확인하도록 하자. 콘솔에 그냥 print 를 해보면 볼 수 있다.

----- api_put @ views.py -----
def api_put(request):    method = request.META['REQUEST_METHOD']
    print method
    print request.GET
    print request.POST
    return HttpResponse('OK', status=200)

이렇게 하고 put.py 로 한번, 브라우저로 리프레시 한번 하면 콘솔에 아래와 같이 찍힌다.

POST
<QueryDict: {}>
<QueryDict: {u'content': [u'THIS IS CONTENT'], u'tag_list': [u"['tag1', 'tag2', 'tag3']"], u'meta': [u'{}'], u'app_key': [u'deadbeef']}>
[12/Jul/2012 21:27:37] "POST /api/v1/put/ HTTP/1.1" 200 2
GET
<QueryDict: {}>
<QueryDict: {}>
[12/Jul/2012 21:27:40] "GET /api/v1/put/ HTTP/1.1" 200 2

귀찮으니

  a. content, tag_list, meta, app_key 가 모두 있지 않은 경우
  b. method=POST 가 아닌 경우

에 모두 예외를 던져버리자. … 코드를 만들고 나서 생각해보니 method=POST 강제하는건 왠지 데코레이터 있을 것 같다. 검색 ㄱㄱ

https://docs.djangoproject.com/en/dev/topics/http/decorators/

빙고. 그러면 최종적으로 api_put 의 모양은 아래와 같게 된다.

----- api_put @ views.py -----
from django.views.decorators.http import require_http_methods
class MissingRequiredField(BaseException): pass                                    

PUT_REQUIRED_FIELDS = ['app_key', 'content', 'tag_list', 'meta']

@require_http_methods(["POST"])
def api_put(request):                                                             
    try:                                                                          
        params = request.POST
            if (set(PUT_REQUIRED_FIELDS) - set(params.keys())):
                raise MissingRequiredField('MissingRequiredField')                                                                                                        
        content = params['content']                                               
        app_key = params['app_key']                                               
        tag_list = params['tag_list']                                             
        meta = params['meta']                                                     
                                                                                  
        print(app_key, content, tag_list, meta)                                   
                                                                                  
        return HttpResponse('OK', status=200)                                     
    except BaseException, msg:                                                    
        print('Exception raised: %s' % str(msg))                                  
        return HttpResponse(str(msg), status=500)          

그런데, 콘솔 출력을 보면

(u'deadbeef', u'THIS IS CONTENT', u"['tag1', 'tag2', 'tag3']", u'{}')
                       
이렇게 보이는게, tag_list, meta 가 직렬화된 상태로 넘어온 것 같다. … 음 이거 귀찮은데 …
그냥 요청을 json 으로 받을까. 일단 get.py 도 만들고 나서 고민하자. get.py 는 put.py 의 copy&paste로 뚝딱.
api_get 도 api_put 을 복사붙이기로 작성한다.

views.py: http://git.dgoon.net/?p=tokebi.git;a=blob;f=tokebi/api/views.py;h=09dd4ab585b3be18654fd51356f1ccd1d7f50fdc;hb=c4a018b479157c9e27a6a67c912079e3ebe1be6d
get.py: http://git.dgoon.net/?p=tokebi.git;a=blob;f=sample_query/get.py;h=4d10dabf8d1097464fad6d15bed582366480a2ca;hb=c4a018b479157c9e27a6a67c912079e3ebe1be6d
put.py: http://git.dgoon.net/?p=tokebi.git;a=blob;f=sample_query/put.py;h=1f2f5ef3aef9be70e6758094b3fcdd238ec33619;hb=c4a018b479157c9e27a6a67c912079e3ebe1be6d

오늘은 일단 여기까지. 추후에 요청을 json 으로 받도록 해보자. … 는 나중에 하고, 간단한 persistent layer를 붙여서 아이폰에서 put/get 동작시키는걸 먼저 해야겠다.


tags : Django, REST, tokebi, web
Trackbacks 0 : Comments 5

2012/07/05: Hello world in xcode

Daily life/Hard study 2012.07.05 22:50

1. 일단 화면에 hello world을 출력하는 간단한 앱을 만들어보자.

2. 텍스트를 입력받고 버튼을 누르면 뒤집힌 텍스트를 출력하는 앱으로 업그레이드 해보자.

-----

Create new project를 누르면 application 타입을 물어본다. 뭐 이런 것들이 있다:
  Master-detail
  OpenGL
  Page-based
  Single view
  Tabbed
  Utility
  Empty

일단 헤딩이니까 Empty로 해보자
  project name: helloworld
  company name: dgoon
  class prefix: DGOON
  - 헐 자동으로 local git 저장소를 만드네?

프로젝트 생성하고 그냥 실행을 하면 (enable developer mode? 를 물어본다)
  run/stop 버튼 오른쪽에 target device를 설정할 수 있는 select box가 있다. ipad는 너무 크니까 iphone 으로 한다
  실행하면 아무것도 없는 하얀 화면이 나옴

프로그렘 진입은 main.m 파일이다. 하는 일은 UIApplicationMain 클래스를 만드는 것 뿐이다.

-> UIKIT_EXTERN int UIApplicationMain(int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName);

DGOONAppDelegate 가 결국에는 모든 일을 다 하는 녀석인데, 이는 DGOONAppDelegate.h/m 파일에 있다.
결국 우리는 UIAppilcationDelegate를 상속받아 클래스를 만들어서 UIApplicationMain에게 던져주면 되는것이다!

DGOONAppDelegate.m 을 보면 몇 개의 함수들이 정의되어 있다.

  didFinishLaunchingWithOptions
 applicationWillResignActive
 applicationDidEnterBackground
 applicationWillEnterForeground
 applicationDidBecomeActive
 applicationWillTerminate

이름을 보면 하는 일들은 명확하다. 각 함수 안에 있는 주석을 읽으면 도움이 되겠지.
나는 didFinishLaunchingWithOptions 에서 화면에 helloworld만 찍어주면 된다.

didFinishLaunchingWithOptions 함수를 보면 self.window 를 만든 다음에 makeKeyAndVisible 을 불러준다.
검색해보니 key window 란 입력 이벤트를 받는 윈도우다. 즉, 화면에 보이게 하고 터치나 키 입력 등의 이벤트를 저 윈도우로 받게 세팅하는 것이다.

self.mytext 를 만들어서 저기에 "helloworld" 를 넣은 후에 보이게 하면 될것같다.

self.window 는 DGOONAppDelegate.h 에 선언되어 있는데,

@property (strong, nonatomic) UIWindow *window;

이런 모양이다. property 를 좀 검색해서 찾아보았다. http://maclove.pe.kr/28 이 문서에 나름 잘 설명되어 있는듯. …
그런데 strong 에 대한 설명은 없다! 검색해보니 최근에 추가된 것 같은데 retain 의 대체이며, reference counting 에 관련된 속성이라 한다.

헤더(.h)에는 이렇게:

@property (strong, nonatomic) UITextView *mytext;

구현(.m)에는 이렇게:

@synthesize mytext = _mytext;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

    self.window = [[UIWindowalloc] initWithFrame:[[UIScreenmainScreen] bounds]];

    // Override point for customization after application launch.

    

    self.mytext = [[UITextViewalloc] initWithFrame: CGRectMake(100, 100, 200, 200)];

    self.mytext.text = @"HelloWorld!";
    [self.window addSubview:self.mytext];

        

    self.window.backgroundColor = [UIColor whiteColor];

    [self.window makeKeyAndVisible];

    returnYES;

}


추가하였다. cmd+b 로 빌드는 성공. cmd+r 로 실행해보면 옆으로 누워있는 아이폰에 HelloWorld! 라고 쓰여있는게 보인다.
cmd+방향키로 오른쪽 왼쪽으로 돌릴 수 있다. textview는 기본적으로 수정 가능한가보다. 클릭을 하면 키보드가 뜨고
키입력을 하면 뭔가 써 넣을 수도 있고, select, copy 등도 가능하다. 수정 못하게 해보자.

    [self.mytext setEditable:false];

mytext생성 후에 이렇게 한줄 넣어주면 된다. xcode의 자동완성이 아주 도움이 많이 된다. 있을법한 이름을 때려 넣으니까 나온다.
수정은 불가능한데 여전히 select, copy 등은 가능하다. 이것도 막고 싶은데 setSelectable 은 없는 듯 하다. 나중에 찾아본다. …

helloworld 찍는 앱 완성.

-----

2번으로 넘어가기 전에, 테스트를 좀 해보자. self.mytext 에 스트링을 바꿔 넣으면 바로 반영이 되는가?
applicationDidEnterBackground 함수에 아래와 같이 한 줄을 넣으면,

    self.mytext.text = @"enter background!";

처음 실행할 때에는 HelloWorld! 가 보이고, 홈버튼을 누른 후에 다시 아이콘을 클릭하면 enter background! 로 텍스트가 바뀐다.
그런데, 다시 시작하니까 화면이 누워있다. 이거 기본 로테이션을 바꿀 수 없나?

왼쪽 project tree view의 루트를 클릭하고,  Target Helloworld -> Info -> Supported interface orientations 에서 item 0/1/2 순서를 바꿔주면 된다. Portrait를 0번으로 놓았다.

-----

2번을 위해서는

  a. UITextView 2개를 넣어서 하나는 입력(a), 하나는 출력(b)에 쓴다.
  b. UIButtonView (가 있나?) 를 만들어서 콜백함수를 작성 -> 자동완성을 보니 UIButton 이 나온다.
  c. 버튼의 콜백함수에서 (a)를 읽어 뒤집은 후 (b)에 넣는다.

를 하면 된다. 수정해보자.

위에서 추가했던 mytext 를 지우고, 대신 이런 아이들을 넣는다.

.h ->

@property (strong, nonatomic) UITextView *input_text;

@property (strong, nonatomic) UITextView *output_text;

@property (strong, nonatomic) UIButton *reverse;

.m ->

@synthesize input_text = _input_text;

@synthesize output_text = _output_text;

@synthesize reverse = _reverse;


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

    self.window = [[UIWindowalloc] initWithFrame:[[UIScreenmainScreen] bounds]];

    // Override point for customization after application launch.

    

    self.input_text = [[UITextViewalloc] initWithFrame: CGRectMake(100, 100, 100, 20)];

    self.input_text.text = @"INPUT";

    [self.input_textsetScrollEnabled:false];


    self.output_text = [[UITextViewalloc] initWithFrame: CGRectMake(100, 200, 100, 20)];

    [self.output_textsetEditable:false];

    [self.output_textsetScrollEnabled:false];

    self.output_text.text = @"OUTPUT";

    

    self.reverse = [UIButtonbuttonWithType: UIButtonTypeRoundedRect];

    self.reverse.frame = CGRectMake(100, 150, 100, 20);

    [self.reversesetTitle:@"REVERSE!"forState:UIControlStateNormal];

    [self.reverseaddTarget:selfaction:@selector(reverse_output) forControlEvents:UIControlEventTouchUpInside];

    

    [self.windowaddSubview:self.input_text];

    [self.windowaddSubview:self.output_text];

    [self.windowaddSubview:self.reverse];

        

    self.window.backgroundColor = [UIColorwhiteColor];

    [self.windowmakeKeyAndVisible];

    returnYES;

}


- (void)reverse_output

{

    self.output_text.text = @"CLICKED!";

}

이렇게 하면 버튼을 눌렀을 때 output_text 가 CLICKED! 로 바뀌는 화면이 만들어진다.
여기서 reverse_output 을 수정해서 입력 텍스트를 뒤집게만 하면 된다. 일단 NSString 에 reverse 메소드가 있는지 확인을 해본다.
없다. … 검색을 해보니 … … ?? 뭐 이리 복잡해 으아앙 멘붕.

그냥 파일 이름을 DGOONAppDelegate.mm 으로 바꾸고 제일 위에

#include <algorithm>

#include <string>

using namespace std;

이걸 추가하고 나서 reverse_output 을 아래와 같이 고쳐버렸다. … C++이 최고다. orz

- (void)reverse_output

{

    std::string s([self.input_text.textUTF8String]);

    std::reverse(s.begin(), s.end());

    self.output_text.text = [NSStringstringWithUTF8String: s.c_str()];

}

이게 한글, 일본어같은 문자열에 대해서는 (당연히) 동작을 안할텐데 일단 이렇게 놓고 오늘은 끝.
어떻게 해야 쉽고 안전하고 이식성 있게 뒤집을 수 있는가!?


'Daily life > Hard study' 카테고리의 다른 글

MongoDB 읽고쓰기  (6) 2012.07.26
도깨비(tokebi) 더미 API  (5) 2012.07.12
2012/07/05: Hello world in xcode  (5) 2012.07.05
An introduction into SICP  (6) 2011.09.03
Reading: How To Design Programs(HTDP)  (0) 2011.07.13
D 프로그래밍 언어 - 오타(?)  (3) 2011.05.12
Trackbacks 0 : Comments 5

An introduction into SICP

Daily life/Hard study 2011.09.03 21:37
SICP는 Structure and Interpretation of Computer Programs 의 약자다. 마법사 책(wizard book)으로도 알려져 있으며, 한때(지금도?) MIT에서 1학년들에게 가르쳤다고 한다. 해커를 동경하거나, 이미 해커이거나, 해커가 되어가고 있는 사람들이라면 LISP의 한 방언인 Scheme의 존재는 알고 있을테고, scheme을 언급할 때 함께 튀어나올 수 밖에 없는 책으로 해커 문화와 뗄 수 없을 것 같... 다고 마음대로 생각한다.

유명세에 비해서 완독한 사람은 생각보다 많지 않은데, 책의 난이도가 - 혹은 읽는데 드는 잉여력이 - 꽤 높기 때문이다. 프로그래밍 스킬과는 별도로 체계적이고 논리적인 사고, 적절한 추상화 능력, 2차 이상의 Higher order 를 상상할 수 있는 능력 그리고 어느 정도의 하드웨어 지식을 요구한다. 거꾸로, 이 책에서 주려는 것이 저런 능력이기 때문에 잉여력만 충분하면 읽으며 깨달아가도 된다. 이런 경우까지 고려하여 이 책을 읽기 위한 최소한의 요구사항은 끈기Scheme 코드를 돌려볼 수 있는 환경이다.

책은 총 5개의 챕터로 구성되어 있으며, 각각

1. Building abstractions with procedures
2. Building abstractions with data
3. Modularity, Objects and State
4. Metalinguistic abstraction
5. Computing with register machines

이런 제목을 가지고 있다. 각 챕터에 대한 간략한 소개를 하자면,

1장:
 스킴의 기본적인 문법부터 시작한다. 프로시져-그러니까, 그냥 함수라고 하자-를 만들고 사용하는 연습을 주로 한다. 만들어진 프로시져 여러개를 조합해서 Compound procedure 를 만들면서 Building block을 쌓는게 이런거구나 느껴보게 된다. 그리고 장 말미에 Procedure 를 만드는 Procedure 를 만드는 Procedure ... 같은 Higher order procedure 의 맛을 좀 보게 된다.

2장:
 1장의 연결이라는 느낌이다. 1장에서 다룬 프로시져 사이를 넘나드는 "데이터"를 조명한다. 그리고, 프로시져와 데이터를 엮어 인터페이스의 생성 - 즉 추상화 배리어가 어떤 의미를 가지는지에 대해서 구구절절 설명하고 실습하게 된다. 그리고 Data-directed programming 이라는 짓을 하게 되는데, 이 부분은 Object-oriented 쪽의 정수와 맞닿아 있다. 거의 타입 시스템과 Message-passing 시스템을 직접 구현해 올리게 되는 (꽤) 놀라운 체험을 하게 된다.

3장:
 1, 2장까지의 Scheme 은 순수한 함수형 언어였다. 모든 프로시저는 referential transparency 를 가지고 있었다. 여기부턴 안그렇다. 시간에 따라 변화하는 상태가 등장하기 시작하며 머리가 조금 아파지기 시작한다. 기초적인 concurrency 에 대한 개념, 따라오는 문제, 이런 문제를 다루는 방법 등에 대해 맛을 볼 수 있다. (미친듯한 회로 시뮬레이션-_-) 그리고 3장 마지막에 나오는 stream - 사실 3장의 핵심은 이거다. Stream이 무엇인가? 어떻게 동작하는가? 에 대한 설명을 하며 다음 장에 본격적으로 등장할 Lazy evaluation 을 준비시킨다.

4장:
 책의 주제가 슬슬 드러나기(난이도가 급상승하기) 시작하는 곳이다. 책 표지에 왜 Apply-Eval 이 있을까? 에 대한 답이 시작된다. 우리는 스킴으로 스킴 코드를 실행시키는 인터프리터를 만들게 된다. 그 와중에 깨알같은 최적화들이 나오게 되고, 일반적인 인터프리터나 컴파일러가 이런 짓을 하겠구나 - 싶은 영감을 좀 얻을 수도 있다. 특히, Evaluator 를 만들고 여기에 Lazy-evaluation 을 적용시키는 작업을 직접 해보면, 꽤 많은 걸 느껴볼 수 있게 된다. 앞에서 주구장창 이야기했던 추상화의 소중함이랄까 ... 그리고, 뒤쪽 두 개의 소챕터는 amb로 설명되는 nondeterministic computing과, logic programming이 어떻게 구현될 수 있는지 맛보기를 보여준다. (STREAM!) 텍스트가 아이디어->구체화->코드, 의 과정을 설명하고 있는데 읽고 나면 간단해 보이지만, 책을 덮고 직접 그 길을 따라가 보면 이 책을 쓴 사람들이 얼마나 미친 내공의 소유자들인지 깨닫게 된다. ...

5장:
 4장에서 우리는 소프트웨어 세상의 끝을 보았다. 4장에서 코드를 실행시키는 코드를(Metacircular) 만들었으니까... 이제 한 단계 아래로 내려간다. Higher order이기 때문에 주의해서 읽어야 한다. 우리는 일반적인 가상의 기계어를 정의한 후에, 이 기계어를 시뮬레이션 하는 스킴 코드를 만들고, 이 기계어로 스킴 실행기를 만들어서, 기계어로 만들어진 스킴 실행기에 스킴 코드를 넣어 실행하는 시뮬레이션을 한다. 그리고 나서, 이번엔 실행기가 아니라 스킴 코드를 우리가 정의한 기계어로 컴파일하는 컴파일러를 만든다. 인터프리터와 컴파일러가 어떻게 다른지, 컴파일러가 만든 코드가 인터프리터에 비해 어떤 최적화를 더 할 수 있어서 빠른건지를 이해할 수 있게 된다. 연습문제들이 앞 3, 4 장에서 만들었던 유틸리티 코드를 많이 재사용하기 때문에 앞 장을 대강 읽었거나 문제를 하나도 안풀고 그냥 넘어왔다면 5장에서 능욕당하게 된다.
 
텍스트와 함께 많은 수의 연습문제들이 배치되어 있는데, 이 책을 제대로 읽은 사람들이 하나같이 "연습문제를 풀어보지 않으면 읽었다고 말할 수 없다"고 이야기한다는 점에 주목할 필요가 있다. 위에서 말한 완독이라는 표현은, 연습문제를 풀어보았다 - 라고 말할 정도를 뜻한다. 참고로 나는 모든 문제를 풀어본 상태는 아니고, 모든 문제에 대해 풀이를 시도해본 정도이다. 몇몇 난이도가 미친듯한(혹은 잉여력 스카우터를 폭발시킬듯한) 문제를 제외하고는 대부분 풀긴 했다. ... 사실 연습문제를 풀지 않으면, 끝까지 읽는게 불가능할지도 모르겠다. 이런 부분이 많지는 않지만, 앞쪽 연습문제에서 다룬 내용을 뒤쪽 텍스트에서 당연하다는 듯이 사용하고 있으니까.

나같은 경우에는, 연습문제를 풀면서 많이 배웠다. 진짜로, 많이. 원래 훌륭한 사람이라면 나만큼 많이 배울게 없을수도 있긴 하다. 어쨌거나 텍스트만 읽을 때 보다 연습문제까지 풀었을 때에 얻을 수 있는게 훠어어얼씬 많다는 것은 확실하다.

이 책을 읽을, 연습문제를 풀 사람들에게 줄 만한 조언은,

1. 1장, 2장은 뒤쪽을 위한 몸풀기다. 여기서 어려움을 느끼면 곤란하기 때문에, 책을 읽으며 순서대로 문제를 풀려는 생각은 버리고 책을 빠르게 여러번 읽으며 내용을 씹어먹으며 연습문제를 푸는게 좋을 것 같다.

2. 3장부터는 바로 실행해보기 귀찮거나 어려운 녀석들이 등장한다. 특히 stream 관련해서 텍스트에 있는 내용이나 연습문제를 돌려보기가 까다로운 경우가 있다. 일단, 스트림 관련 코드가 나오면 sicp 홈페이지에서 코드를 구하던가 아니면 문제는 스킵하고 텍스트에 있는 코드를 먼저 입력해서 "실행해볼 수 있는" 환경을 만든 후에 문제를 푸는게 좋다. 실행 환경을 먼저 만드는 것은, 이 후 책 끝까지 언제나 중요하다. 

3. 일단 풀어보거나, 혹은 구체적인 접근 방법을 떠올리기 전에 다른 사람의 풀이를 보지 않는다.

4. 삽질 기록을 어딘가(연습장이나 위키, 그 외 어디라도) 정리해 가기를 강추. 코드도 함께 남기면 나중에 앞 장의 코드나 풀이 기록이 필요할 때(4장부터는 앞쪽을 다시 읽어보게 되는 경우가 많다) 큰 도움이 된다. 나중에 앞을 다시 봤는데 기억이 안나고 기록도 없으면 책을 다시 읽고, 연습문제를 다시 풀어야 할지도 모른다.

5. 직접 만들어도 되고 있는 것을 써도 되는데, 테스트 코드 작성을 추천. 1, 2, 3장까지는 특히나 엄청나게 매우매우 유용하다. 나는 컴키드님 자작 테스트 툴을 썼는데 큰 도움이 되었다. 4장 이후로는 테스트가 곤란한 것들이 많아서 유명무실 ...

6. Pencil & Paper 를 항상 가까이에. 

특히 실행 환경 구성이 꽤 시간을 많이 잡아먹고, 우리를 지치게 하는 부분이다. SICP와 함께 자주 거론되는 책으로 HTDP(How To Design Programs)가 있는데, 이 책 저자들은 SICP를 타산지석 삼아서 - 실제로 SICP의 악명으로부터 HTDP가 나왔다고도 한다 - HTDP에서는 테스트해볼 수 있는 환경 구축에 신경을 매우 많이 썼다. 심지어 SICP 연습문제 풀이에도 MIT Scheme 보다 Dr.Racket(PLT Scheme) 이 더 나을 정도.

SICP를 씹어먹고 나면 CS, 혹은 프로그래밍 세계에서 자주 나오는 여러가지 개념들에 대해 보다 깊은 이해를 할 수 있게 된다. 왜냐, 거의 대부분의 주제를 (기초적인 수준이지만) from scratch 로 삽을 떠 보기 때문에 ...

몇 년째 SICP를 읽고 문제를 풀고 있다고 하면 재귀나, 고차함수 같은거 실제 필드에서는 쓸일 없는데 알아서 뭐하냐, 라는 태클이 간간히 들어오기도 했다. 여기에 대해 답을 하자면 재귀, 고차함수 이런게 실제로 프로덕션 코드에 쓰이는 일이 있는지 없는지 나야 잘 모르지만, 그게 중요한 게 아니다. 중요한 것은 생각의 도구/단위를 얻는 것이다. GOF 디자인 패턴 책에 설명된 패턴 중 상당수가, 함수가 1차 객체인 많은 언어들에서는 큰 의미가 없어진다. 언어의 패러다임을 이해하고 적절한 개념을 갖추고 있는 일반적인 개발자에게는 너무 당연한 스킬이라서. 자바에서 몇백 라인에 걸쳐 십수개의 클래스를 정의해가며 구현한 패턴들이 파이썬이나 루비, 루아 혹은 스킴에서는 단 몇 줄로(가끔은 One-liner)해결되는 경우가 꽤나 많다.

적절한 개념을 탑재하고 있으면, 적절한 개념이 지원되는 언어로 적절한 코드를 만들 수 있다. 그 개념이 자연스럽게 지원되지 않는 언어라면, 그 개념을 흉내내는 바탕을 깔(배운 용어를 써보자면, 추상화 단계를 만들) 수 있다. 이런 삽질들이 모여서 새 언어가 만들어지고, 기존 언어의 스펙이 확장되고, 여러가지 프레임웍들이 생겨나는 것이다.

문법을 알고 있는 아무 언어나 골라서, 이 언어로 쓰여진 규모 있는 오픈 소스 프로젝트의 저장소를 하나 받아와보자. 언어의 문법을 알고 있는 사람이라면 누구나 이 코드를 읽을 수 있다. 하지만 언어의 문법을 알아도 1년동안 이 프로젝트의 코드베이스를 이해하지 못하는 사람이 있는 반면, 몇 시간이면 대강의 구조를 파악하고 재미삼아 코드 수정까지 하는 사람이 있다. 코드 한 줄 한 줄이 모여 만들어내는 보다 큰 논리단위의 구성을 읽는 능력의 차이인데, 다른 말로 일반적으로 쓰이는 추상화 블럭들을 읽어내는 능력이다. 한 권의 책 치고, SICP가 커버하는 프로그래밍의 핵심 블럭들은 굉장히 넓은 범위에 분포되어 있으며, 아마 자주 쓰이는 대부분의 개념에 대해 맛을 볼 수 있으리라 생각한다.

SICP를 읽으면 뭘 할 수 있게 되요? 혹은 어떤 내공이 쌓여요? 라고 묻는다면, 간단하게 이렇게 대답하련다. 코드 말고 프로젝트 단위의 읽고 쓰기를 위한 내공을 조금 얻을 수 있다고. 물론, 이 책 말고 다른 곳에서도 얻을 수 있다. 얻어야 하는 것이 무엇이건 간에 도달하는 방법은 여러가지일 수 있다는게 이 책의 중요 포인트 아니던가.

'Daily life > Hard study' 카테고리의 다른 글

도깨비(tokebi) 더미 API  (5) 2012.07.12
2012/07/05: Hello world in xcode  (5) 2012.07.05
An introduction into SICP  (6) 2011.09.03
Reading: How To Design Programs(HTDP)  (0) 2011.07.13
D 프로그래밍 언어 - 오타(?)  (3) 2011.05.12
SICP study: 근황  (3) 2011.04.27
tags : SICP, 리뷰, 추상화
Trackbacks 1 : Comments 6

Reading: How To Design Programs(HTDP)

Daily life/Hard study 2011.07.13 21:52
SICP와 자주 비교되곤 하는 HTDP를 읽으며 천천히 문제 풀이 중이다.

확실히 SICP보다는 좀 더 상냥한 책이다. SICP를 읽으며 항상 아쉬웠던 부분인 "코드를 실행해 볼 수가 없어!" 라는 포인트를 정확하게 짚어내어 공략했다. 언어에 따라가는 Dr. Racket 이라는 개발 환경에서 Teach-pack 이라는 개념으로 대부분의 예제/문제 코드를 GUI 까지 있는 상황에서 실행해볼 수 있다. 그리고 다루는 주제들이 SICP보다 훨씬 평이하다. SICP에서 Newton method, Fixed-point 이런걸 보며 떡실신한 사람이 꽤 있는데(그 중 1인), 여기서는 비슷한 곳에서(책의 백분위 위치로 볼때) 숫자 맞추기 게임 정도의 예를 사용한다.

당연하다면 당연한데, 단점으로는 too verbose, too slow 라는 느낌. 열심히 공부해야지! 하며 불타오르지 않는... 다지만, 그건 SICP 를 몇년째 계속 보고 있어서 그렇게 느껴지는걸지도 모르겠다. ㅠㅠ

밀당같은거 하지 말고 입문자를 확실히 잡기 위해서라면 HTDP 를 먼저 읽게 하는게 나을 것 같다는 생각이 든다.

뭐랄까, 경시대회나 올림피아드를 준비하고 싶어? 그럼 SICP를 보렴. 내신/수능 성적을 위한거라면 HTDP 를 보렴. 이런 느낌이랄까? 절반 이상 읽고 나면 SICP 와 다시 한번 비교하는 글을 써 봐야지.

'Daily life > Hard study' 카테고리의 다른 글

2012/07/05: Hello world in xcode  (5) 2012.07.05
An introduction into SICP  (6) 2011.09.03
Reading: How To Design Programs(HTDP)  (0) 2011.07.13
D 프로그래밍 언어 - 오타(?)  (3) 2011.05.12
SICP study: 근황  (3) 2011.04.27
예술의 전당 수묵화반, 전시회 준비  (3) 2010.10.21
Trackbacks 0 : Comments 0

D 프로그래밍 언어 - 오타(?)

Daily life/Hard study 2011.05.12 15:02
22 page:

line.startswith(" ") 처럼 보이는 두 개는 각각
line.startswith("    ") - 4칸, line.startswith("  ") - 2칸

이다. 공백 여러개가 하나로 보여서 혼란에 빠질 수 있다.


262 page:

class Widget { ... }
final class UltimageWidget : Widget { ... }
class PostUltimateWidget : UltimateWidget { ... } // 오류 !!!

빨간색 Ultimate 부분이 빠져있다.

 
Trackbacks 1 : Comments 3

SICP study: 근황

Daily life/Hard study 2011.04.27 00:48

http://mitpress.mit.edu/sicp/full-text/book/book.html

이제 4년차다. 슈레인은 뭐 그리 느리냐고 줄창 까대지만, 흔들리지 않고 꿋꿋하고 느긋하고 여유있게 나가고 있다. 본래 우리 스터디의 수장이신 지아님의 뜻이 널럴하고 오래가는 스터디 아니었던가!

대강 훑어서 읽은 걸로는 수십번을 읽었고, 문제를 풀면서 뒤적뒤적, 앞에 했던거 기억이 안나서 돌아가 다시보기, 뒤에 나오는걸 미리 가져다 쓴다고 하면 뒤에 가서 보고 오기 ...

이제 5.1장을 막 덮었다. 괄호 범벅인 코드로 어셈코드와 이걸 해석하는 어셈블러, 레지스터 머신을 만들고 있으니 기분이 묘해진다.

여기까지 모든 문제를 다 푼 건 아니다. 내맘대로 스킵한 것도 있고, 풀고 정리하지 않은 것도 있긴 하지만 - 적어도 모든 문제를 다 읽었다고는 말할 수 있다. 풀지 못했거나, 잘못된 풀이를 내놓았더라도 문제가 무엇을 물어보는지, 무엇을 알아야 풀 수 있는지, 내가 어떤 시도를 했었는지에 대해서는 말할 수 있다는 정도?

아무래도 여름 즈음에는 일단 끝까지 다 볼 것 같다. (이후엔 HTDP를 좀 볼까 ... 생각중)

그러니 이제 슬슬 회고: 이 책이 나를 어떻게 바꿨는지, 내가 SICP를 읽으면서 어떤 성장을 했는지, 혹은 문제를 푸나 안푸나 별 차이 없었을지 - 에 대해서 정리 준비를 해야 하는데, 잘 모르겠다. 으앍 ㅋ 가장 크게 느껴지는건 꾸준히 공부하는 습관을 들인거, 스터디 멤버(대표적으로 지아대장님을 비롯한 컴키드님, 지송님, 솔리드원님, 야라님, 그리고 스쳐지나가셨던 모든 분들 + 옆 스터디(응?) 멤버들 ㅋㅋㅋ)들과 친해진 거 두가지인데, 사실 얘네들은 SICP와는 직접적인 관계가 없으니까 빼고 ... ... ...

분명 내가 몇년이나 질질 끌면서 이 책을 읽고 문제를 깨작거린 이유가 있을텐데, 이걸 깔끔하게 정리해야 한다. 그래야 다른 사람한테 추천할 수 있겠지?

으으, 고민해보자.

-----

http://wiki.dgoon.net/doku.php?id=sicp:structureandinterpretationofcomputerprograms
http://code.sicp.or.kr
http://wiki.sicp.or.kr

어쩌다보니 sicp.or.kr 을 내가 들고 있다. ( ... )
Trackbacks 0 : Comments 3

예술의 전당 수묵화반, 전시회 준비

Daily life/Hard study 2010.10.21 00:32
뭐 그리지 ... ? 엔써문화쎈타에서 고민하면서 붓펜으로 이것저것 깨작깨작. 이런 꽉찬 느낌으로 해볼까...



Trackbacks 0 : Comments 3

지름: Prog. Clojure - 허접한 이북리더지만 쓸모는 있다

Daily life/Hard study 2010.08.05 01:07
Qty Description Shipped Price
1 SHCLOJ-P-00: Programming Clojure (eBook) 08/04/10 $21.00
Sub total: $21.00
Tax: $0.00
Grand total (US$): $21.00

그래서 지름 ...
Trackbacks 1 : Comments 8

소나무 연습

Daily life/Hard study 2010.06.28 10:19

옆에 뭔가 더 그리고 싶은데 ... 사진에는 정말로 뒤는 그냥 하늘이다. 절벽 꼭대기에 있는 소나무라서 ...

Trackbacks 0 : Comments 0

숙제를 하고 싶었는데...

Daily life/Hard study 2010.04.16 01:10
에디 결혼식 때문에 토욜에 못갈듯 하다.

... 숙제를 하기 위해 일단 연습용 화선지에 붓펜으로 쓱쓱쓱.

연습 좀 해본 다음에 붓으로 그려야지.

숙제

그림이 허접한건 순전히 내가 왕초보라서 입니다. 절대 붓펜 탓이 아님.


아마 현실에는 존재하지 않는 풍경이겠지. 수묵화 선생님이 연습용으로 만든 가상의 경치다. ... 보고 베껴 그려오라고 하셨는데 ...

... 어렵잖아!?

ㅠㅠ

일단 여기 부끄러운 증거샷을 남기면 더 열심히 연습하겠지? ㅋㅋㅋ


'Daily life > Hard study' 카테고리의 다른 글

지름: Prog. Clojure - 허접한 이북리더지만 쓸모는 있다  (8) 2010.08.05
소나무 연습  (0) 2010.06.28
숙제를 하고 싶었는데...  (0) 2010.04.16
처음 그려본 소나무  (7) 2010.03.21
숙제, 그리고 그 후 ...  (0) 2010.03.19
부랴부랴 숙제중  (0) 2010.03.19
Trackbacks 0 : Comments 0

처음 그려본 소나무

Daily life/Hard study 2010.03.21 00:06
선생님이 그려주신 걸 참고로 - 늘어진 소나무 - 직접 그려본 첫 그림.

소나무를 "그리는" 것 보다, 실제의 소나무 혹은 소나무 사진 등을 "관찰하는" 것이 더 중요하다.


'Daily life > Hard study' 카테고리의 다른 글

소나무 연습  (0) 2010.06.28
숙제를 하고 싶었는데...  (0) 2010.04.16
처음 그려본 소나무  (7) 2010.03.21
숙제, 그리고 그 후 ...  (0) 2010.03.19
부랴부랴 숙제중  (0) 2010.03.19
디군이 가장 많이 산 책: 전도서에 바치는 장미  (2) 2009.12.22
Trackbacks 0 : Comments 7

숙제, 그리고 그 후 ...

Daily life/Hard study 2010.03.19 23:38
OTL

이... 이거 어렵잖아! 생각만큼 어려워! ㅠㅠ


숙제 진행중... 여기저기 보이는 이상한건 무시하자 ㅋㅋㅋ


여기저기 널부러진 연습지들... 아... 어렵다. ㅠㅠ

그래도 오늘은 여기까지.

'Daily life > Hard study' 카테고리의 다른 글

숙제를 하고 싶었는데...  (0) 2010.04.16
처음 그려본 소나무  (7) 2010.03.21
숙제, 그리고 그 후 ...  (0) 2010.03.19
부랴부랴 숙제중  (0) 2010.03.19
디군이 가장 많이 산 책: 전도서에 바치는 장미  (2) 2009.12.22
안드로메다 #1  (2) 2009.12.05
Trackbacks 0 : Comments 0

부랴부랴 숙제중

Daily life/Hard study 2010.03.19 21:30
토요일 오전 예술의 전당 서예교실 수묵화반 반장 디군


아흑... ㅠㅠ 숙제 시작. 소나무를 그립시다


'Daily life > Hard study' 카테고리의 다른 글

처음 그려본 소나무  (7) 2010.03.21
숙제, 그리고 그 후 ...  (0) 2010.03.19
부랴부랴 숙제중  (0) 2010.03.19
디군이 가장 많이 산 책: 전도서에 바치는 장미  (2) 2009.12.22
안드로메다 #1  (2) 2009.12.05
코딩도장: 회고  (1) 2009.01.09
Trackbacks 0 : Comments 0

디군이 가장 많이 산 책: 전도서에 바치는 장미

Daily life/Hard study 2009.12.22 15:31
지난 일요일, 친구 Y와 송년 모임 겸 내년 스터디(?) 계획을 위해 수원에서 티타임을 가졌다. 수원역 어딘가(...)에서 커피를 한잔 했고, 그 전에 북스리브로(그러니까, 서점)에서 책 구경을 잠시 했다. Y가 책을 보는 동안, 나는 뭐 재밌는거 없나 - 하는 기분으로 평소 내가 읽고 싶었던 책들이 있는 곳에 가서 책 구경을 했다.

일리움, 푸코의 진자, 해저 2만리, 은하수를 여행하는 히치하이커를 위한 안내서, 전날의 섬, 걸리버 여행기, 이상한 나라의 앨리스 - 이런 책들이 어딘가에 있었고 ... 그 와중에 ...

전도서에 바치는 장미

가 있었다. 이 책은 ... 로저 젤라즈니의 중/단편의 모음이며, "전도서에 바치는 장미" 라는 타이틀은, 수록 작품 중 말도 안되는(과학 소설이라기엔...) SF(Super Fantasy?) 중편의 제목이다.

... 그리고 디군이,

산다
친구에게 준다
산다
친구에게 준다
산다
친구에게 준다
...

이런 반복을 꽤나 많이 했던 책이기도 하다. 그리고 어느 순간부터 양장본(그러니까 - 하드커버)이 없어져서 나를 슬픔에 빠뜨린 책이기도 하고. 최소한 다섯권 이상은 샀던 것 같다. ... 가물가물해서 기억이 잘 안나지만. 잘 뒤져보면 군대에 있던 시절 영문 텍스트를 구해서 중/단편 몇개를 통째로 프린트해둔 철도 집 어딘가에 있을듯(화이올리를 사랑한 남자/The man who loved faioli, 는 거의 외울정도). 이랬던 그녀(그책?)가!

나름 하드커버로 이쁜 표지를 가지고 책장에 떠억하니 꽂혀있는거다. 우왕~ 그래서, 아마 누군가에게 다시 줘 버릴지도 모르겠지만, 한권  또 사버렸다. 그리고 집에 오는 길에, 전철에 앉아 수록된 첫번째 소설(중편인가 단편인가...) "12월의 열쇠" 를 다시 읽었다.

좋아하는 음악을 몇달 내내 틀어놓고 사는 느낌이 이런걸까. 로저 젤라즈니의 글들은, 장편과 중/단/장(손바닥!)편들이 문체/정서가 매우 다른데 - 나는 그렇게 생각 -, 나는 이런 짧은 글들에서 뿜어져 나오는 젤라즈니님의 감수성이 좋다.

우왕! 크앙!

'Daily life > Hard study' 카테고리의 다른 글

숙제, 그리고 그 후 ...  (0) 2010.03.19
부랴부랴 숙제중  (0) 2010.03.19
디군이 가장 많이 산 책: 전도서에 바치는 장미  (2) 2009.12.22
안드로메다 #1  (2) 2009.12.05
코딩도장: 회고  (1) 2009.01.09
후기: SICP 스터디모임 10월 정모  (4) 2008.10.26
Trackbacks 0 : Comments 2

안드로메다 #1

Daily life/Hard study 2009.12.05 15:29

체크인: 기분, 기대하는것

D: 기대기대+_+, Activity 가 뭔지 알고시펑
Y: 이런건 처음? 안드로이드 동작 과정을 알고싶다
-----

따로 책을 보기보다는 여기(http://developer.android.com/guide/topics/fundamentals.html) 문서들을 읽으면서 따라가기로 한다. 영어로 되어 있지만 공부도 할겸... 다 사람이 쓴거다, 사람이 읽을 수 있다.

여기부터 두서없는 기록.

안드로이드 어플리케이션은 각 리눅스 프로세스 하나+JVM 을 가지고 그 위에 올라간다. 어플리케이션이라고 해도 그저 프로세스일 뿐이다. 사용자가 느끼는 어플리케이션과는 단위가 약간 다르다. 앞으로는 안드로이드 어플리케이션은 그냥 컴포넌트라고 부르기로 하자. 컴포넌트는 4가지 종류가 있는데,

Activity, Service, Broadcast receiver, Content provider

얘네들이다. Activity는 화면을 가진 페이지 하나-정도의 개념이고, Service는 화면 없이 돌아가는 프로세스이다. 예를 들어 간단한 게임을 만든다고 할 때, 게임 플레이가 진행되는 화면이 하나의 Activity, 열심히 배경음악을 연주하는 보이지 않는 프로세스가 Service. 이렇게 하나의 어플리케이션은 여러개의 컴포넌트로 구성된다.

Broadcast receiver는 시스템 이벤트, notification 등을 받는 녀석. Content provider는 내장 SQLite 에 접근하기 위한 녀석이다. 실제 테스트/연습용 프로그램들에서는 Activity, Service 두개를 자세히 보면서 시작하면 될 듯.

Intent는 컴포넌트들 사이를 오가며 정보를 전달하는 일종의 사자(Messenger) 이다.

사용자가 느끼는 어플리케이션 단위는, 여러개의 Activity가 Stack 으로 구성된 TASK 라고 볼 수 있다. Task 는 4가지 종류가 있는데,

Standard, SingleTop, SingleInstance, SingleTask

이런 아이들이 있다. 별도로 지정하지 않으면 Standard. 각 태스크 타입마다 태스크의 Activity가 들어가고 나오는 동작이 조금씩 다르자. 자세한건 문서 참조.


-----
회고: 좋았던거, 안좋았던거, 다음에 할거

D: 코드를 보면서 문서를 읽어서 좋았다. 와~ 코드를 써가면서 문서를 읽으면 좋을텐데 ... 오늘 읽은걸 다 돌려보셍
Y: 생각보다 영어공부를 많이했다. 우 ~ 클래스 실체를 확인을 못한게 아쉽. Overview를 보고 백그라운드 음악을 돌려보고싶음.

이 장소를 Daum지도에서 확인해보세요.
서울특별시 서초구 서초4동 | 토즈 강남대로점
도움말 Daum 지도
Trackbacks 0 : Comments 2

코딩도장: 회고

Daily life/Hard study 2009.01.09 00:12
오랜만에, 정말 오랜만에 코딩도장 모임에 다녀왔습니다. 오늘은 책 소개도, 코딩 모임도 아닌 회고 모임이었는데요... 사실 애자일 회고를 제대로 읽어본 적이 없는데,

애자일 회고를 읽어오시면 좋습니다

라는 공지글을 보고, 사람들이 책을 읽고 올테니 가서 쪽쪽 빨아먹자! 라는 생각을 번뜩 하고는 후다닥 참석한다고 손을 들었습니다. ... 네, 네, 제가 원래 이렇게 삽니다. 흐흐흐흐 ~

모임에는 열명 내외의 분들이 참석해 주셨습니다. (열 한...명?) 전에 봤던 분도, 첨 보는 분도 섞여 있었네요. 기억력이 나빠서 담에 볼때 기억 할 수 있을런지... 약 30분 정도의 뜸들이기 시간을 가진 후에 코딩도장 관장, 아샬님이 고고싱 하셨습니다.

- 회고 준비: 각자 소개, 느낌, 왜 나왔는지에 대한 이야기
- 자료 수집: 모임(or 모임 밖이어도...)에서 있었던 일들, 느낀것 정리
- 통찰: 수집된 자료를 정리, 분류. 5-Whys - 다섯 단계의 "왜?" 라는 물음을 통해 근원으로의 여행을 ...
- 무엇을: 전 단계에서 얻은 "이유"에 대해서, 해법/대응을 서로에게 묻고 답함
- 마무리: 누군가에게 감사, 궁금한거 하나, 무엇을 실천할까 골라골라 ~

정도의 순서로 진행이 되었습니다. 주사위의 등장이라던가, 5-Whys 의 시도 같은 것은 왠지 임팩트가 있었습니다. 앞으로도 한동안 계속 떠오를 듯. 하지만, 시간의 부족으로 보다 심도있는 대화를 나누지 못한 것은 좀 아쉬웠습니다. 더욱이 시간의 부족이 모임을 예정보다 늦게 시작하게 되어서 - 의 영향도 있기 때문에 좀 더 아쉽네요. - 제가 마지막에 던진 궁금한거? 에 대한 대답은 왜 항상 늦게 시작할까? 였습니다. ;-)

개인적으로 나왔던 실천강령(???) 중 마음에 들었던 것, 혹은 그럴싸한데? 라고 생각했던 것은,

- 알고리즘 문제보다는 실용적인 문제를!
- 문제 풀어오는 사람에게 쿠폰을 줄까? - 문제 내는 사람한테도 주면...?
- 문제 여러개를 내자 - 근데 누가? ㅠ_ㅠ
- 사회적 기여 - 어떤 ... 모임이 있다고 한다

정도였던 것 같습니다. 적지는 않아서 기억이 가물가물(벌써ㅠ_ㅠ)

일단 까먹기 전에 남기는 후기는 이걸로 완료 ;-)
Trackbacks 1 : Comments 1

후기: SICP 스터디모임 10월 정모

Daily life/Hard study 2008.10.26 23:02
이번에는 2시간짜리, 짧고 굵은 모임이었습니다.

역시 대장이신 지아님과 컴키드님, GSong님, 솔리드원님, 꼬니님, 그리고 저 D군 이렇게 여섯명이 모였습니다. 지아님, 컴키드님과는 좀 일찍 만나서 점심을 같이 먹었고, 다른 분들은 토즈로 바로 오셨네요. 사보텐이 좀 비쌌습니다. ... 만 카드깡 와중에 500원 정도의 수익을 냈습니다. 꺄핫 >_< ... (하지만 나중에 ...)

그리고 2시부터 토즈에서 모임이 시작되었습니다.

체크인, 월간회고 따위는 패쓰. 오늘의 하이라이트는 컴키드님의 "유닛테스트, 하지 않겠는가?" 라는 발표였습니다. 컴키드님 Presentation 에 소질이 좀 있으신듯. 내용도 내용이지만, PPT 한장 한장이 참 인상깊었습니다. ;-) 발표자료는 여기에 있습니다.

그리고 발표 와중에 나왔던 TDD, Unit test등에 대한 쌓였던 이야기들이 참 흥미로웠습니다. 사실 저도 TDD로 일컬어지는 이야기들에 대한 거부감이 없는건 아니었는데 ... =_= 이번 기회를 통해서, 내가 무얼 모르는 것인지를 알 수 있었습니다. 유익한 시간이었어요.

좀 아쉬웠던건, 이렇게 떠들만한 떡밥이 훌륭하게 던져졌는데 두시간밖에 시간이 없어서 ... 라고 하지만, 사실 던킨으로 이동해서 떠들 수 있었기 때문에 나쁜건 아니었어요. 오히려 발표타임과 떠드는 타임이 나뉜게 좋을지도 모르겠군요. ;-)

그리고, 중간에 잠깐 이걸로 모두에게 웃음을 선사할 수 있었습니다. 히힛 ~

... 그리고, 토즈가 할인에서 적립으로 바뀌는 바람에 카드깡 와중에 약 2500원 정도의 손해가 생겼습니다. 점심에 500원 벌었던걸 다 까먹고도 더 까먹었군요. :'(

왠지 짧고 굵은 느낌이 드는 모임이었습니다.

ㅎㅎㅎ

이걸로 날림후기 완료. 사진은... 어떻게 전달하는게 좋을까효 ... =_=

'Daily life > Hard study' 카테고리의 다른 글

안드로메다 #1  (2) 2009.12.05
코딩도장: 회고  (1) 2009.01.09
후기: SICP 스터디모임 10월 정모  (4) 2008.10.26
SICP 스터디모임 9월 정모 늦은 후기  (6) 2008.09.30
Firefox plugin: 분홍이+Scrapbook >_<=b  (2) 2008.08.03
SICP 스터디 7월 정모  (5) 2008.07.29
Trackbacks 3 : Comments 4

SICP 스터디모임 9월 정모 늦은 후기

Daily life/Hard study 2008.09.30 22:59
아, 아, 지난 9월 27일(토요일) SICP 스터디 모임의 9월 정기모임에 다녀왔습니다. ... 8월 모임이 없었기에 두달만의 회동이었죠.

7월 모임에서 미쳤던 D군이 Map/Reduce에 대한 발표를 하겠습니다, 라고 질러버려서 덜덜덜 떨던 시간들... 본래대로라면 8월 모임에서 발표를 했어야 하는데, 훌렁 건너뛰고 9월 모임에서 하게 되었습니다. ... 하지만, 이바닥의 생리가 그렇듯이 준비 기간은 의미 없고, 발표 전 2-3일 정도 후다닥 준비해서 결국 날림으로 해치웠습니다. 두달간의 부담을 이틀만에 후딱 털어내버리고 뻔뻔하게 웃음짓고 있는 D군. - 그래서 체크인 단어가 뻔뻔 이었습니다 >_</ 아쉽게도 개인적으로 쓰던 위키가 죽어버려서(SenA RIP) 자료가 없군효. 언넝 다른곳 어딘가에 둥지를 틀고 위키를 다시 올려야겠습니다. ㅠㅠ

참석자는 대장이신 지아님과, 자주 보던 컴키드님, 솔리드원님, 그리고 저 D군 - 이 올드페이스(늙어보인다는 뜻 아님!)였고, 뉴페이스로 8con님, Yarra님, 신글님이 오셨네요. - Yarra님 블로그 찾으려 스토킹중... >_<

토론 주제를 즉석에서 정해서 뭔가 깔쌈한게(하나 있긴 했지만 비밀) 별로 없었던게 좀 아쉬웠습니다.

  • 최근 읽은 책
  • * 공부하세요?
  • 테크트리

이야기들이 나왔습니다. 사실 이날 교보문고에 3번... -_-; 갔는데(어쩌다 보니), 세번째는 모임 이후였지요. 모임에서 나왔던 책들을 몇 찾아볼 수 있었습니다. 컴키드님이 말씀하셨던 독서법에 대한 책이 베스트 셀러 근처에 있더군효.

그리고 여기에 [언제|왜|어디서|어떻게|누가?|무엇을] 공부하세요? 에 대한 답을 각 한문장씩으로 정리합니다. ;-)

  • 언제? 보통 아침 회사일 시작 하기 전 한시간 정도. 혹은 주말에 약속 없을때. 밤에는 공부 안한다.. =_=
  • 왜? 내가 아는 세상을 넓히는 것은 여행과 마찬가지이다. 난 죽을때까지 새로운 땅을 밟아가련다.
  • 어디서? 차 한잔이 손에 잡히는 곳. 평일에는 아무도 오지 않은 새벽의 회사에서, 주말에는 던킨/커피빈/스타벅스 같은곳에 짱박혀서.
  • 어떻게? 평일 아침에는 보통 코딩도장의 문제를 풀거나, 찾아봐야 해 - 라고 적어두었던 것들을 좀 찾아보고 정리하거나, 위키에 제목만 달아놓은 문서를 계속 써나가거나. 주말에는 보통 짱박혀서 연필과 종이를 손에 쥐고 책을 읽는다.
  • 누가? - 음, D군은 보통 혼자 공부한다.
  • 무엇을? 한시간 이내의 코딩문제는 보통 UVA, TopCoder스타일의 문제 - 요즘에는 코딩도장 문제 - 를 해본다. 책은 SICP, Introduction to IR 정도를 읽고 있다. (G.E.B. 는 읽는다고 하기 쫌 뭐하지) 전공 이외의 공부로는 일본어를 하는 척만 하다가 좀 본격적으로 시작해보려고 준비중.

그리고 테크트리에 대한건... 사실 애매하죠. 답도 애매했습니다. 좀 안습... :'(

이후에 어딘가에 있는 커피볶는 집에 짱박혀서 이런저런 수다를 떨다가 제가 사람들을 낚아서 데리고 나왔습니다. ;; 신글님이 이런 저런 질문들을 하셨는데, 제가 평소 가지고 있던 고민들이랑 비슷한 물음들을 많이 던지셔서 인상깊었습니다.

... 이후에는, 위에 이야기한대로 교보문고에 잠시 들렀습죠. 거기서부터 몸이 좀 안좋아서 뻗었슴둥. ㅠㅠ



'Daily life > Hard study' 카테고리의 다른 글

코딩도장: 회고  (1) 2009.01.09
후기: SICP 스터디모임 10월 정모  (4) 2008.10.26
SICP 스터디모임 9월 정모 늦은 후기  (6) 2008.09.30
Firefox plugin: 분홍이+Scrapbook >_<=b  (2) 2008.08.03
SICP 스터디 7월 정모  (5) 2008.07.29
지름: GEB  (0) 2008.07.19
Trackbacks 1 : Comments 6

Firefox plugin: 분홍이+Scrapbook >_<=b

Daily life/Hard study 2008.08.03 21:55
얼마전에 분홍이를 새로 들이고 나서, 오늘 분홍이와 함게 첫 나들이가 있었다. 3셀 배터리라서 오래 쓸 수는 없는 상황 ... 게다가 무선랜도 여의치 않고 ... 해서 일단 나의 사용 패턴을 생각해 보았다. 내가 분홍이를 들고 나가서 할 일이 뭘까?

  1. SICP Online book 읽기
  2. SICP 문제 풀기

... 밖에 없다. =_= 그 외에도 할 수 있는건 많겠지만, 기본적으로 그 외에는 별 관심이 없기 때문에 ... ;; 본래 구입 목적이 스타벅스나 던킨에 짱박혀서(여름엔 시원하고 겨울엔 따뜻한!) 공부하는것 아니었는가.

해서 초큼 생각을 해보니, 난 스터디모임 정모같은데 가지 않으면 어딜 가서도 무선랜을 쓸 필요가 별로 없다. -_-; 배터리 아깝게시리(사실 블루투스도 그닥...) ... 그래서 SICP 온라인 북을 그냥 오프라인에 저장해놓고, PLT Scheme을 설치하기로 결정.

여기서 완전 감동.

ScrapBook이라는 플러그인이 있다. 예전 WebZip이나 기타 다른 프로그램이 하던 일을 해주는데(Static page를 로컬에 저장) 링크까지 쫓아가서 착실하게 저장해 주신다. 나처럼 정적 페이지를 주로 보는 사람이 쓰기에는 좀 짱인듯. >_<=b 설치는 이걸로...

... 그리하야 ...

오늘 전철에서 초큼 오래(한 40분?) 앉아있게 되어서 한번 꺼내놓고 읽던걸 계속 읽어 보았는데. . . 이건 소감.

  1. 바람이는 LCD가 상당히 밝다. 전에 쓰던 놋북(물론 지금도 쓴다 - Dell Latitude D630)의 최대 밝기가 바람이 70% 정도밖데 되지 않는다. ... 최대 밝기로 하니 눈이 부시다. -_-; 전철에서는 20%~30% 정도 밝기만으로도 충분했다.
  2. 무선랜/블루투스/스피커 Off. 화면밝기 30% 정도로 쓰니 두시간 쓰고도 배터리가 20%정도 남는다. 음... 동영상도 안보고, 무선랜도 거의 안쓰는 나에게는 3셀이면 충분할듯.

배터리 부분은 초큼 고려가 필요한데, 용도에 따라 큰 차이가 생길 수 있기 때문이다. 이동중 동영상을 보고 싶다면 3셀은 쥐쥐. ... 6셀로 가야한다... 지만, 10인치 놋북인 관계로 기차나 버스에서 앉지 않고서는 쓸수가 없다. 이동+동영상 조합이라면 PMP를 쓰는게 오히려 나을거고... 어딘가에 짱박혀서 -> 무선랜(or Wibro?)으로 웹서핑을 하거나 동영상을 보고 싶다면? 3셀로는 절대 못한다 -> 6셀이나 어댑터가 필요.

나같은 경우는 분홍이를 한번 꺼내서 약 1-2시간 정도를 널럴하게(SICP 페이지 띄워놓고 읽다가, PLT Scheme으로 코드 쓰고... 만든 코드는 저녁에 집에 와서 SenA로 커밋) 쓰고 다시 가방에 넣는 사용 패턴을 가지고 있으므로 3셀이 이상적이다.

... 게다가, 곧 가방에 어댑터 대신 외장배터리가 들어갈 예정. 바람이를 꺼내서 쓰다가, 가방에 넣으면서 외장배터리에 연결해서 외배->3셀 로 충전 고고싱. 다시 꺼내쓰고, 가방에 넣으면서 충전 고고싱... 패턴.

외배가 오면 외출할때 (여행 빼고) 절대 어댑터를 들고 나가지 않겠다! >_<///

쓰고보니 플러그인 이야기는 없군. 제목을 분홍이 사용소감으로 바꿔야 하나... -_-;

'Daily life > Hard study' 카테고리의 다른 글

후기: SICP 스터디모임 10월 정모  (4) 2008.10.26
SICP 스터디모임 9월 정모 늦은 후기  (6) 2008.09.30
Firefox plugin: 분홍이+Scrapbook >_<=b  (2) 2008.08.03
SICP 스터디 7월 정모  (5) 2008.07.29
지름: GEB  (0) 2008.07.19
후기: 코딩도장 오프모임  (2) 2008.06.16
Trackbacks 0 : Comments 2

SICP 스터디 7월 정모

Daily life/Hard study 2008.07.29 23:01
지난 토요일, 모종의 코딩 할 꺼리가 있었지만 생각보다 일찍 끝나서 ... SICP 7월 정모에 얼굴을 비칠 수 있었습니다. 살짝(?) 늦었지만, 뭐 그닥 문제는 아니었네요.

... 예고한대로, 분홍이가 왔기 때문에 후기를 씁니다. 그런데 아직 키보드에 익숙해지지가 않아서 좀 힘들어요. 으워어어 ~ 그러니 날림으로 짧게 쓰고 마치겠습니다. 캬캬캬 ~

올드페이스: 지아님, 컴키드님, gsong님, D군!
뉴페이스: soonoh님, 하야로비님

처음부터 그랬지만, SICP 스터디 모임의 정모는 SICP를 읽느라 지친 개발자들이 잠시 쉴 수 있도록 여유를 제공하는 자리이므로 절대 SICP 에 대해서 토론하지는 않습니다. 이번에도 역시 마찬가지였네요.

체크인 - 초심, 패닉 등등 여러가지 나왔습니다. 전 그냥 "우쭐우쭐" 이었네요. 지난 한달은 나름 열심히 문제를 풀었다지요.
발표, 토론 - 제가 최근에 삽질하면서 했던 생각인 Statement, Expression은 뭐가 다른건가? 를 잠시 이야기했습니다. 그리고 책 이야기들을 했어요. 저는 할말이 없어서 SICP! 라고 한번 외쳐 주었습니다.

배운뇨자가 되신 지아님을 보며, 저도 책도 좀 보고 공부도 좀 하고 배운총각이 되어야겠다고 생각했습니다. ;-)

여튼, 그렇게 짧은(실제로 짧았습니다) 토즈에서의 모임을 마치고 (무늬만) 북카페인 나무그늘로 이동해서 수다를 떨었습니다. 하야로비님의 게임 이야기들이 재미있었어요. ... 개발자 모임과 오덕 모임은 0.96 정도의 상관도가 있는 것 같습니다. ;-) 이야기를 해보니 우리나라에서의 게임 개발은 누구나 한번쯤은 생각해보거나 거쳐가는 관문... 혹은 테크타다 보면 반드시 들르는 곳 - 정도 된다는 느낌을 받았네요. ^^

그리고 저는 후다닥 soonoh님과 종각으로 떳습니다. soonoh님이 모임에서는 이야기가 별로 없으셨는데, 버스를 타고 가면서 회사 이야기를 좀 들었습니다. 재미있는(혹은 배울만한게 많은?) 회사에 계신 것 같아요. 다음에 더 흥미로운 이야기들을 들을 수 있을 것 같습니다. 모두 기대 ;-)

---

여기부턴, SICP와 상관 없지만, 전 그날 소백산맥을 마신 듯 합니다... :'( 쿨록쿨록

'Daily life > Hard study' 카테고리의 다른 글

SICP 스터디모임 9월 정모 늦은 후기  (6) 2008.09.30
Firefox plugin: 분홍이+Scrapbook >_<=b  (2) 2008.08.03
SICP 스터디 7월 정모  (5) 2008.07.29
지름: GEB  (0) 2008.07.19
후기: 코딩도장 오프모임  (2) 2008.06.16
후기: SICP 스터디 모임 6월(+5월) 정모  (5) 2008.06.15
Trackbacks 4 : Comments 5

지름: GEB

Daily life/Hard study 2008.07.19 00:05
사야 할 책 리스트에서 하나를 꺼내 사버렸음.

도서상품권 1만원권 + 여타 할인 -> 13620 원에 원서를 샀다. 양장본이 있는지는 모르겠지만, 내 손에 들어온 것은 일단 막 봐도 상관없는 페이퍼백.

GEB

간지좔좔 GEB


후배 슈레인에게 원서가 얼마나 어려운지, 번역은 잘 된건지 물어보고... 강남 교보에서 한번 비교해 보았는데,

한글이 더 어렵다
번역서가 더 비싸다: 원서 26XXX 원, 번역서 20000 X 2 (상,하)

는 이유로 원서 고고싱 했음.

'Daily life > Hard study' 카테고리의 다른 글

Firefox plugin: 분홍이+Scrapbook >_<=b  (2) 2008.08.03
SICP 스터디 7월 정모  (5) 2008.07.29
지름: GEB  (0) 2008.07.19
후기: 코딩도장 오프모임  (2) 2008.06.16
후기: SICP 스터디 모임 6월(+5월) 정모  (5) 2008.06.15
SICP 스터디 모임 4월 정기모임  (3) 2008.04.26
Trackbacks 0 : Comments 0

후기: 코딩도장 오프모임

Daily life/Hard study 2008.06.16 20:20
아래 있는 SICP 스터디 6월 모임 - 에서 솔리드원님의 소개로 코딩도장의 오프라인 모임에 참석했습니다. 모임 이름은 코딩도장이었지만, 제가 참석한 모임은 "책 소개" 모임이었네요. 순수문학도 있고, 수학 관련된 녀석도 있고, 메타-사고에 관한 책도 있었습니다.

저까지 포함해서 다섯명이었는데, 도장의 관장님 한분(아샬님)과 엔에스알, Pod, SolidOne님이 참석하셨습니다. 아샬님은 제일 마지막에 오셨는데, 다른 분들이 롸커같다고 이야기해서 어떨까 했었는데 딱 오시니 아, 그렇군 이라는 생각이 들더군요. ;-)

모임에서도 이야기 했지만, 물 흐르듯이 스르륵~ 자연스럽게 진행되어서 참 좋았습니다. 관장님의 능력이겠지요. 크게 세션을 나누면 책에 대한 이야기와 모임의 방향에 대한 이야기, 두 개가 되겠습니다.

: 각자 읽은 책을 보여주고 설명하고 공유한 다음, 많은 사람의 관심을 얻은 녀석에 대해 좀더 in-depth로 까보는 ... 의도였지만, 시간이 꽤 많은데다가 읽지 않은 상태로 추천(?)된 책이 있어서 모든 책을 돌아가면서 이야기했습니다. 저는 준비되지 않은 관계로 넘어갈...리가 없으니, 그나마 가장 최근에 읽었던 리만가설을 꺼냈습니다. ... 읽긴 읽었는데 리뷰조차 쓰지 못하고 있는 고로 한번 더 읽어야 하는 책이지요. :'(

  • 아샬님: 아이디어 머신, 효과적인 문제해결 - 두권 모두 Meta-Thinking 이라는 범주로 넣고 싶군요.
  • 솔리드원님: 수학으로 생각한다 - 수학 교양서와 전공서의 중간쯤에 있는 듯 합니다. ... 아 전투요정 유키카제도 있었습니다. ... 이거 나올줄은 몰랐다능 -0-/
  • 엔에스알님: 익스트림 프로그래밍 Explained, 국경의 남쪽 태양의 서쪽, 스키니 비치 - Extreme programming은 읽어봤던 책인데, 역시 다른 분들이 읽고 하는 이야기를 들으니 새로운걸 깨닫게 됩니다. 역시 돌멩이는 부딪쳐야 불꽃이 튀나봅니다. 국경의 남쪽 태양의 서쪽에 대해서는 하루키와 류씨의 책에 대한 이야기들이 나왔습니다. ... 상실에 대한 이야기가 인상깊군요. 스키니 비치는 그냥 패스했습니다. >_</
  • Pod님: 컨버전스 컬쳐 - ... 잘 모르겠어염 ㅠ_ㅠ 아직 별 느낌이 없었습니다. Hybrid와 Convergence가 뭐가 다를까요? 라는 의문이 생겼습니다.
  • D-goon: 리만가설 - ... 저 스스로 떡실신.

대충 이렇게 정리할 수 있겠군요. 다른 분들의 지식을 훔치는 풍요로운 시간이었습니다. 그리고, 두번재 세션에서는 모임에 대한 이야기를 했습니다. 왠지 써놓고 보니 그 전타임에 뛰었던 SICP 스터디 모임에서와 비슷한 느낌이 드는군요. ;-)

모임의 방향: 사실 저는 이 오프모임이 처음이었기 때문에, 할 수 있는 말이 별로 없... 었습니다만, 그래도 왠지 어색하지 않아 이런 저런 이야기를 꺼낼 수 있어 좋았습니다. 앞으로 오프 모임이 활성화 될 조짐이 있는데, 저도 가보고 싶... - 긴 합니다만, 시간과 위치가 안좋으면 낭패... =_=a

몇 가지로 느낌을 정리하자면,

  1. Proactive members
  2. 다양성에 대한 갈망
  3. 깊이에의 추구

이런 것이 인상깊습니다.



... 일단 온라인에서 코딩 문제를 풀다가 적당한 때와 장소를 기다려 보면 되지 않을까 합니다. ;-)


'Daily life > Hard study' 카테고리의 다른 글

SICP 스터디 7월 정모  (5) 2008.07.29
지름: GEB  (0) 2008.07.19
후기: 코딩도장 오프모임  (2) 2008.06.16
후기: SICP 스터디 모임 6월(+5월) 정모  (5) 2008.06.15
SICP 스터디 모임 4월 정기모임  (3) 2008.04.26
SICP 스터디 모임 늦은 후기  (4) 2008.03.27
Trackbacks 0 : Comments 2

후기: SICP 스터디 모임 6월(+5월) 정모

Daily life/Hard study 2008.06.15 23:35
실제로 문제 풀이는 지지부진하지만(핑계는 있습니다만... 없는 무덤이 어디 있겠습니까 -_-;) 그래도 나름 Heartbeat을 유지하고 있는 구글 그룹스의 SICP 스터디 모임의 6월 정모에 다녀왔습니다. 5월에 정모를 하지 못해서, 6월 정모를 좀 일찍 하고 5+6월 합동 오프 모임이라고 볼 수 있습니다. 장소는 강남 토즈, 선발대들은 오무토토마토에서 점심을 함께 먹으며 지난 모임때의 쿠폰 사건(ㅠ_ㅠ)과, 최근의 촛불 정국에 대한 이야기들을 나누었습니다. ^^

일단, 모임의 참석자는 지아님, D군(The Me!), 솔리드원님, 꼬니님, gsong님 이었고, 컴키드님이 중간에 난입(?) 하셔서 자리를 빛내 주셨습니다. ;-)

흐름은 체크인 -> 월간회고 -> 발표(The Me! 가 LaTeX에 대해 짧게 설명했습니다) -> 자유토론, 이었습니다. 간단하게 각 단계에서의 임팩트를 짚고 넘어가도록 하겠습니다.

체크인: 모두 오늘의 각오(?)랄까, 한 Word, 혹은 Phrase로 표현하며 운을 떼는 것입니다. 다른 분들이 백지, 내려놓자, 가능성, 촛불 등의 멋있는 단어들을 선택하시는 와중에, 저 역시 있어 보이기 위해 용자를 택했습니다. 이렇게 공부도 지지부진하고, 다른 분들에게 자극적인걸 던져주지도 못하며 정체되어 있는 안타까운 상황을 어떻게 뚫고 나갈 것인가 - 를 생각하다 떠오른 단어입니다.

다시 한번 밑줄 쫙 - 뚫고 나갈 것인가 - 에서 그래 이거다! 라는게 떠올랐습니다. 여기서 아하! 떠오르지 않으신다면 천원돌파 그렌라간을 한번 보실 것을 강권합니다. 어디서 구해야 할지 모르겠다 싶으시면 저를 찌르셔도 됩니다. ;-) ... 여튼! 공부도 용자처럼 해야 합니다. 그것이 우리 SICP단(?)의 방식입니다! 좌절과 나태로 점철된 스터디의 역사에 바람구멍을 뚫어놓을 용자! ... +_+ 비록 하루짜리 열정이라도 말입니다. ;-)

월간회고: 모두모두 이제 미안하고 또 미안하다 못해 미안하지조차 않게 되어버린 경지에 이르러 있었습니다. 좀 가슴아프기도 하지만, 이런 문제의식의 공유가 이 후 자유토론의 씨앗이 되었던 것 같습니다. 그리고 들어보니 모두들 훌륭한 핑계(?)가 있었단 말이지요. 대장이신 지아님이 "우리는 공부 (강제로) 권하는 사회가 되지 말자" 라는 비전을 제시했고, 대부분이 따라가기로 했으니 그닥 큰 문제는 되지 않는 것 같습니다. 공부는 스스로 하는거지요. ... (추가하자면, 위에 말했듯 용자처럼 스스로!)

발표: 제가 약 30분 정도 LaTeX 사용에 대해서 이야기를 했습니다. 발표라고는 했지만 그냥 위키 페이지 띄워놓고 간단하게 설명하면서 터미널에서 latex 문서를 만들고 pdf 파일로 뽑아내는 과정을 보여드린 것 뿐이었습니다. 제가 일단 latex에 익숙하지 않아서 핵심을 제대로 짚어줄 수 없어서 좀 안타까웠습니다. 최근 인터뷰 보러 다니느라 정신줄을 놓고 있다가 준비를 제대로 못해서 ... =_=

그래서, 다음달에 반성하는 의미로 발표를 하나 더 하기로 했습니다만, 좀 애매합니다. 이건 자유토론과 연관되는 것이니 뒤로 패스.

자유토론: 스터디 모임의 방향성, Goal 에 대한 이야기들이 많이 나왔습니다. 큰 줄기 몇개를 챙겨보면,

  • 대화가 필요하다. 어떤 종류의 소통이던 간에, 대화가 있어야 모임에 피가 흐른다.
  • 누가 "저 모임에 가면 뭘 얻을 수 있니?" 라고 물었을 때, 대답할 것이 있어야 한다.
  • 고기동성을 위해 모임 내에서 Sub-모임이 나타나 공대 만들어 사냥하고 다니는 것도 좋다.
  • 스터디+문제풀이 외에 프로젝트라던가, 공부를 도와줄 수 있는 프로그램 구성을 만들어봤으면?

이런 것들이(더 많았습니다...) 있었습니다. 무얼 할지 정확하게 정해지지는 않았지만, 저 네번째 것 - 스터디+문제풀이 이외의 코드 작성 - 에 대하여 "제가 할께요. 알아서 페이지 만들어서 뭔가 정리해 나가고, 다음달에 발표해 보겠습니다." 라고 발언해 버렸습니다. ... 용자라는 단어 때문일까요, 한 5분정도 엄청 Proactive member가 되었었는데 딱 그 5분에 삘받아서 손들어 버렸습니다... ㄷㄷㄷ 이제 발언에 책임을 지기 위해 한동안 고생좀 해야 할 것 같습니다. ... =_=

다음달 모임은 7월 중순이나 말 정도로 예상되는데요 ... 그 전까지 뭔가 가시적인, 혹은 가시적이지는 않아도 우쭐댈 정도는 되는 결과가 나오길 바랍니다.



그리고 모임 후에, 저는 솔리드원님을 따라서 광화문 쪽에 있다는 다른 (어둠의 비밀결사같은 느낌을 주었던) 모임에 갔었는데 ... 이곳도 대단히 흥미로운 경험이 되어서, 후기를 별도로 작성해야 할 것 같습니다.



;-) 다른분들 후기 기다려요 ~


'Daily life > Hard study' 카테고리의 다른 글

지름: GEB  (0) 2008.07.19
후기: 코딩도장 오프모임  (2) 2008.06.16
후기: SICP 스터디 모임 6월(+5월) 정모  (5) 2008.06.15
SICP 스터디 모임 4월 정기모임  (3) 2008.04.26
SICP 스터디 모임 늦은 후기  (4) 2008.03.27
지름: SICP  (8) 2008.02.23
Trackbacks 3 : Comments 5

SICP 스터디 모임 4월 정기모임

Daily life/Hard study 2008.04.26 23:29
오늘 SICP 스터디 모임의 4월+두번째 정기모임이 있었습니다. 사실, 전 요즘 좀바빠서 아무것도 준비하지 못했어요... ㅠㅠ 그래서 참으로 미안한 마음을 안고, 그렇지만서도 뻔뻔하게 가긴 갔습니다.

오늘 참석자는 D군을 포함해서 총 다섯명이었습니다. + 지아님, 컴키드님, 명명님, 솔리드원님

오무토 토마토에서 오므라이스를 먹고 강남 토즈로 이동 후 이야기를 시작했습니다. 순서는 이야기꺼리 하나씩 적어내기 -> 체크인 -> 4월 회고 -> 컴키드님의 마인드맵 발표 -> 이야기꺼리 하나 잡아서 토론 -> 다음 이야기꺼리 잡아서 토론 -> 장소이동(어딜까요? ㅎㅎ) 후 연습문제 1.19 풀어보기, 였습니다.

지난 모임때와 같은 장소, 같은 시간이었지만 사람이 절반으로 줄어서 실제로 한명 한명이 떠들었던 시간은 훨씬 길어졌습니다. 영양가 있는 이야기는 아닌 것 같았지만, 그래도 뭔가 많이 떠들었다는게 만족스럽네요. 으히히~ 지난번엔 한마디씩 두바퀴 도니까 그냥 시간이 다 되어버려서 좀 아쉬웠단 말이죠... =_=a

각 과정에 대해 한마디씩만 정리하면,

체크인에서는 대부분(?)의 분들이 공부 못해서 미안해 - 라며 죄책감에 시달리는 모습을 보여주셨습니다.
4월 회고에서는 공부하면서 느낀것+어려웠던 것 들에 대한 이야기가 좀 나왔습니다.
마인드맵 발표에서는 툴의 사용법, 생각을 정리하는 방법(흩어놓은 후 묶는다) 등에 대한 이야기가 있었습니다.
토론꺼리 하나 - Top-down, Bottom-up 방식의 차이, 사고의 방향에 대해 이야기가 나왔습니다. 해결해야하는 과제가 있을때 양쪽을 왔다갔다 하게 된다는 이야기도 했군요. - 이때쯤 명명님이 가셨던 것 같습니다.(가물가물)
토론꺼리 둘 - 훌륭한 사람이 되려면? 지금보다 나은 사람이 되려면? 어찌해야 하는가, 에 대한 이야기들이 있었습니다. - 이걸 마치고 솔리드원님이 가셨군요.
그 후 노닥거리면서, 회사이야기들이 좀 나오다가, 연습문제 1.19를 한번 풀어보고 나서 남은 세명이 빠이빠이 ~

평소에 듣기 힘든 이야기들이 많이 나와서 좋았습니다. 사실 이런 이야기 할 기회가 흔한건 아니거든요. ;-)

그리고... 정말 오랜만에 블로그에 글을 씁니다. 일단 월욜만 지나고 나면 한숨을 돌릴 수 있을테니, 빨리 그날이 지나길... (이라지만 월욜 이후에는 지구에 없을지도 ㅠㅠ)

추가: 다음 정모(5월)때 D군이 latex 사용법에 대해 발표합니다. ;-) 우어어어 ~~~

'Daily life > Hard study' 카테고리의 다른 글

후기: 코딩도장 오프모임  (2) 2008.06.16
후기: SICP 스터디 모임 6월(+5월) 정모  (5) 2008.06.15
SICP 스터디 모임 4월 정기모임  (3) 2008.04.26
SICP 스터디 모임 늦은 후기  (4) 2008.03.27
지름: SICP  (8) 2008.02.23
지름: Programming Erlang(pdf)  (2) 2008.02.17
Trackbacks 2 : Comments 3

SICP 스터디 모임 늦은 후기

Daily life/Hard study 2008.03.27 22:22
SICP를 프린트해서 보다가... 최근에서야 책을! 샀다. 그리고 이번엔 좀 제대로 문제를 모두 풀어서 정리하겠노라! 라고 생각하던 차에... 검색하다 SICP 스터디 모임이라는 그룹을 발견해서 후딱 가입했다. 모르는게 있으면 물어봐야지 ~ 라고 생각했었는데, 왠걸 그 모임도 이제 막 시작한거였다. ... 뭐, 생각해보니 더 좋다. 함께 풀어가는게 어찌 즐겁지 않겠는가? ;-)

그리고, 지난 토요일 아기다리 고기다리 던 대망의 첫 정모가 있었다. 원랜 바로 후기를 썼어야 하지만 회사에서 좀 바빠서 치이다 보니 제대로 정리도 못하고 오늘에서야 ... 지아님 죄송해요 ㅠ_ㅠ

여튼, 지아님 카리스마 아래 줄줄이 - 십여명의 개발자들이 모여서,
  1. Check-in: 모임의 감상을 한마디로
  2. 월간회고
  3. 구글 그룹스 사용법 by comkid
  4. Intermission: 1.13 증명 by Me!
  5. 앞으로의 스터디 방향
에 대한 이야기를 했다. ... 사실, 업계에 진짜로 나온지는 채 몇 달이 되지 않기 때문에 나와 다른 곳에서 프로그래밍을 하고 있는 사람을 만나본 일이 별로 없어서 기대 만빵이었다. 자바를 다룬다는 분, 임베디드(우왕>_<=b)쪽 일을 하신다는 분이 계셨고... 웹 쪽도 있었던 듯? 체크인때도 한 이야기지만, 웹에서 보던 사람들이 NPC가 아니라 정말로 세상에 존재하는 사람들이라는게 참 신기하다. 새로운 사람들과의 만남은 항상 (즐거운 것 만은 아니고 괴로울 뿐인 것 또한 아니지만) 짜릿하다.

일단 첫 모임은 자기 소개라던가 그룹 사용법 등 일반적인 이야기들이 주가 되었는데 ... 다음 모임에서는 뭔가 주제나 문제를 잡아서 이야기를 하게 된다고 한다. 히히, 약간 기대 >_</

삼천포로 빠지면 어쩌나 걱정도 좀 했었는데, 지아님이 잘 이끌어 주셔서 알찬 시간이 되었던 듯 하다. 아쉬운 것은 카메라.... ㅠ_ㅠ 와 3시간이 너무 짧았다는 것. 그리고 마우스와 키보드 USB 케이블을 들고가지 않았던 것 반성합니다... ㅠ_ㅠ

아아... 약발을 바로 받았어야 하는데 회사 일로 흐름이 끊겨서 좀 걱정이다. ... 일단 오늘까진 놀고 내일부터 다시 달려봐야지. ...

모두 반가웠어요!

'Daily life > Hard study' 카테고리의 다른 글

후기: SICP 스터디 모임 6월(+5월) 정모  (5) 2008.06.15
SICP 스터디 모임 4월 정기모임  (3) 2008.04.26
SICP 스터디 모임 늦은 후기  (4) 2008.03.27
지름: SICP  (8) 2008.02.23
지름: Programming Erlang(pdf)  (2) 2008.02.17
책(TAOCP, ConcreteMathematics) 도착!  (5) 2007.11.22
Trackbacks 1 : Comments 4

지름: SICP

Daily life/Hard study 2008.02.23 16:10
그동안 ... 그냥 인쇄해서 보던 문서(...)하나를 결국 책으로 사버렸음. 뒤져보면 꽤 이래저래 사연이 많은데 ...

계기 1. 문화상품권이 갑자기 들어왔다.
계기 2. 내가 알고 있던 가격보다 훨씬 싼 물건(!)이 떳다.

SICP는 뭐, 다 알겠지만, Structure and Interpretation of Computer Programs 의 줄임말이다. MIT에서 컴퓨터 처음 배우는 애들한테 읽게 시킨다는 것으로 유명하며, 수많은 초보 공학도들을 돌고래에게 무릎꿇게 만들었다고도 한다... -_-; 번역서는 최근에서야 나왔고(무려 ht**w*** 님이 번역에 참여하셨다니?), 저게 미친듯이 읽고 싶었을 당시에는 돈이(군인이었다) 없어서 부대 물품을 좀 ... 해서 인쇄해서 봤었다. 최근엔 회사에서 한번 더 인쇄해버렸는데 ... 그래도 책으로 사서 책장에 좀 꽂아두고 싶은 녀석이라 이번 기회를 잘 노렸지! 히히 ~

교보문고에서 SICP 2/E, HardCover가 3만5천원에 뜬 것을 약 10분쯤 전에 확인. 바로 질렀음. 이해는 못하겠지만 동일한 책(SICP 2/E, HardCover 이면 똑같은거 아닌가? ISBN-10 넘버도 확인했음. 같더라)이 정가 9만얼마에 할인가 7만 얼마로 있던데 ... -_-;

뭐, 대강 한번 읽고 연습문제도 (거의) 풀어보긴 했지만, 그래도 Wizard 한 녀석 정도 서가에 키우는것도 좋을듯 하다. 일단 간지 좔좔 - >_<v 그나저나, 읽는 속도보다 책을 사는 속도가 더 빨라서 낭패다. 주말에 좀 달려야 할듯.

사용자 삽입 이미지


- 하지만 안타까운 것은, 인터넷 교보문고는 문화상품권 결제를 지원하지 않는다. ... 는 것과, 기왕 사는 김에 하면서 Amazon에서 "The Complete Peanuts" 를 검색해서 장바구니에 지금 넣어버렸다는거... 당연히 아마존도 상품권 못씀. 결국 상품권은 기스 하나 없이 그대로 ... ㅠㅠ

P.S. 책 표지 이미지는 인사이트(http://www.insightbook.co.kr/)에서 가져왔음. (죄송, 무단...;) 얼마전 JCO에서 경품으로 받았던 책(사랑하지 않으면 떠나라! - 다 읽고 리뷰 ㄱㄱ)도 이제보니 인사이트다. 출판사명 기억해두겠음.

'Daily life > Hard study' 카테고리의 다른 글

SICP 스터디 모임 4월 정기모임  (3) 2008.04.26
SICP 스터디 모임 늦은 후기  (4) 2008.03.27
지름: SICP  (8) 2008.02.23
지름: Programming Erlang(pdf)  (2) 2008.02.17
책(TAOCP, ConcreteMathematics) 도착!  (5) 2007.11.22
지름: TAOCP, CONtinuous disCRETE Mathematics  (2) 2007.11.16
Trackbacks 0 : Comments 8

지름: Programming Erlang(pdf)

Daily life/Hard study 2008.02.17 01:39
올해는 Erlangio 두 개를 공부한다. 아주 능숙하게 다룰 필요는 없음. 지금 루비나 파이썬에 익숙한 정도까지 툴을 익히고, 언어의 철학을 곱씹는다. 그 중 Erlang 먼저 시작. 책 질렀음. 역시 pdf로 질렀으니 제본하자. ;-)

... 내가 pdf로 사는걸 즐기는 까닭은, 페이지 아래쪽에 Prepared exclusively for Kangsan Lee 라고 뜨는게 좋아서임. 히히 ~ 여튼 아래는 기념샷 !


사용자 삽입 이미지

'Daily life > Hard study' 카테고리의 다른 글

SICP 스터디 모임 늦은 후기  (4) 2008.03.27
지름: SICP  (8) 2008.02.23
지름: Programming Erlang(pdf)  (2) 2008.02.17
책(TAOCP, ConcreteMathematics) 도착!  (5) 2007.11.22
지름: TAOCP, CONtinuous disCRETE Mathematics  (2) 2007.11.16
토익점수 나왔음  (0) 2007.05.22
tags : Erlang, PDF, 지름
Trackbacks 0 : Comments 2