정상혁정상혁
  • 업데이트

    • 2019.04.10 : 현시점에서는 Calepin 서비스도 종료되었습니다.

    • 2013.03.05 : GIST에 썼던 글을 옮기어 봅니다.

      • Springnote 서비스 종료 전에 쓴 글이라서 Springnote의 마이그레이션 코드는 이제는 쓸수가 없네요)

자료를 정리하는 형식과 도구에 대한 고민

회사에서 만든 자료들은 MOSS(Microsoft Office SharePoint Server)나 위키, 메일함 등이 있으니 큰 아쉬움은 없는데, 개인적인 자료들은 어떻게 정리하고 보관, 공유할지가 늘 고민이 됩니다.

저는 공개하고 싶지 않은 것들은 에버노트드롭박스를 쓰고, 공유할 자료들은 스프링노트와 이글루스 블로그을 활용하고 있습니다.코드들은 주로 GithubGist에 올립니다.

이렇게 다양한 서비스를 쓰다보니 자료를 서비스 간에 이동을 할 일이 생기면 많이 불편합니다. 예를 들면 에버노트에 있는 자료를 블로그에 올린다거나 스프링노트에 있는 글을 구글사이트로 옮길 때가 그렇습니다.그냥 한 두 페이지면 복사해서 붙여넣기를 하겠지만, 여러 페이지를 옮길 때 그렇게 하면 단순 반복 작업에 들어가는 시간이 아깝습니다.웹 브라이저나 웹 에디터의 차이 때문에 칸 맞추기 같은 사소한 편집에 시간을 많이 허비하기도 합니다.웹이나 클라언트 환경 등 다양한 환경에서 모두 편집이 간편했으면 하는 바램도 있었습니다. 우분투를 쓰는 시간이 많다보니 MS워드처럼 특정 OS가 종속적인 편집기는 별로 좋아하지 않기도 합니다. 그리고 정리한 내용을 자주 찾아보니 검색과 탐색 기능을 쓰기 쉬웠으면도 했습니다.

이번에 스프링노트가 종료될 예정이라 어떤 형식으로 자료를 정리할 것인지에 대한 고민이 더 커졌습니다.

요구사항을 정리하면

  • 자료를 이동하는 비용이 적어야하고 (이식성)

  • 자료의 모양 때문에 편집을 하는 시간이 되도록 없었으면 하고

  • 특정 환경에 종속적이지 않은 다양한 문서 편집기를 사용할 수 있어야 하고

  • 검색 기능을 간편하게 쓸 수 있어야 한다.

이런 점들을 고민하다보니 우선 정리하는 자료의 형식은 마크다운을 선택하게 되었습니다. Github와 Gist를 쓰면서 그 단순함에 만족을 했고, 텀블러, 트렐로등 지원하고 있는 서비스도 늘어가고 있어서 이식성도 높다고 생각했습니다.

그리고 원본 데이터를 파일단위로 관리하고 싶었습니다. 쓰던 서비스가 망하더라도 파일을 단순히 복사해서 다른 서비스로 옮길 수 있다면 이전 비용이 적을 것이기 때문이였습니다.

Github를 이용한 Octopress도 검토했으나, 버전관리까지는 필요없이 최종 파일만 관리하면 되었기 때문에 Dropbox를 썼으면 좋겠다고 생각했습니다.

사람 생각은 다 비슷한지, 이미 Dropbox + Markdown을 이용한 블로그와 위키 서비스가 나와 있었습니다.

Calepin( =Markdown + Dropbox)로 블로그 만들기

Calepin을 이용해서 http://book.benelog.net이라는 블로그를 만들어봤습니다.그동안 여러 곳에서 정리하던 읽은 책에 대한 메모를 Markdown으로 정리한 것입니다.

아래와 같이 .md 파일의 처음 시작에 날짜와 제목, URL, 태그 등의 정보를 달아주고, 파일을 [Dropbox 공유폴더]/Apps/Calepin이라는 폴더로 복사합니다.

Date: 2012-07-19
Title: 어제를 버려라
Slug: 어제를_버려라
Tags: IT업계

그리고 Calepin 사이트에 가서 'Publish’버튼을 누르면 약속된 폴더에서 파일정보를 읽어와서 블로그를 생성해 줍니다.

calepin

이 정보를 바탕으로 블로그를 만들어줍니다. 아래와 같이 굉장히 단순한 디자인인데, 저는 이 정도로 충분하다고 느꼈습니다.

book benelog net

scriptogr.am라는 서비스도 거의 비슷한 기능을 제공하는데, 블로그 테마 등 약간의 커스터마이징이 가능합니다. wikipackit도 사용법은 유사하고, 위키 페이지간의 링크 기능을 제공하지만, 현재까지는 페이지를 비공개로만 관리할 수 있습니다. 앞으로 유사한 서비스가 더 많이 나올수도 있고, 이전 비용도 크지 않으니 언제든지 좋은 서비스가 나오면 갈아탈 생각입니다.

스프링노트 마이그레이션

저는 스프링노트로 관리하던 페이지들 중 일부는 http://wikipackit.com 으로 옮겼습니다. 스프링노트의 API를 이용해서 Markdown형식으로 스프링노트 페이지를 다운로드하는 스크립트를 작성했습니다.Python과 Pandoc을 활용했습니다.

Eclipse Markdown Text Editor

편집기로는 주로 Eclipse에서 Markdown Text Editor을 사용하고 있습니다.Eclipse가 무거운 편이지만, Platform Runtime Binary를 찾아서 최소한 도로 설치하면 50MB가 넘지 않게 비교적 가볍게 쓸 수도 있습니다. 예를 들어 Eclipse 3.8의 최소설치를 위한 파일은 아래 링크에서 찾으실 수 있습니다.

편집을 하면서 HTML렌더링을 할 수도 있고, Eclipse의 단축키를 그대로 활용할 수도 있습니다. 예를 들어 파일을 찾을 때 Ctrl + Shift + R키를 눌러서 파일명의 일부를 입력해서 바로 찾아가는 것들이죠.

eclipse markdown plugin

이렇게 구축한 글쓰기와 자료정리 환경을 굉장히 만족하면서 쓰고 있습니다. Markdown을 지원하는 서비스나 도구들은 앞으로 계속 나올 듯하고, 스스로 만들기도 쉽기 때문에 더 편하게 쓸 수있는 가능성도 크다고 봅니다.

정상혁정상혁

Andrian Walker라는 영국 개발자가 만든 라이브러리를 제가 수정해서 Github에 올렸습니다.

이 프로젝트에 대한 자세한 설명은 아래에 있습니다.

(github wiki에 적고 블로그에 붙여넣기를 하니 코드 블럭이 깨지는 등 편집이 쉽지가 않네요. 그래서 링크로 대신 합니다.)

위 글의 핵심 내용은 아래와 같습니다.

  • Java에는 여러 줄에 걸친 문자열을 선언하는 문법이 없어서 긴 문자열을 편집하는 작업이 불편합니다.

  • 이를 보완하는 방법을 찾던 중 Adrian Walker라는 개발자가 만든 Multiline-string 이라는 라이브러리를 발견했고, Eclipse에서도 쓸 수 있도록 코드를 수정해서 원저자의 허락을 받고 Github에 올렸습니다. (https://github.com/benelog/multiline)

  • 이 과정 중에 Annotation Processing과 ECJ(Eclipse compiler for Java)에 대해서 알게 된 것들을 정리했습니다.

  • 이 라이브러리와 비슷한 기능을 Lombok에 추가하거나, 안드로이드에서 annotation Processing을 활용할만한 방안을 더 연구해볼만 합니다

정상혁정상혁

계속 이어지는 인사이트 문제입니다.

image

문제

코딩 인터뷰 완전 분석』210쪽 17.3 변형 문제

자연수 n을 입력받고, n!의 계산 결과 중 마지막에 붙은 연속된 0의 개수와 연속된 0 바로 앞에 나오는 숫자를 구하라.

실행 예

input n: 15
output: 3 8

설명

15!은 1307674368000이므로, 마지막에 연속된 0은 3개이고, 바로 앞의 숫자는 8이다.

조건

  • n의 범위는 1 이상, 10000 이하입니다.

  • 테스트 입력은 다음과 같습니다.

20! = 2432902008176640000
30! = 265252859812191058636308480000000
40! = 815915283247897734345611269596115894272000000000
50! = 30414093201713378043612608166064768844377641568960512000000000000
100! = 93326215443944152681699238856266700490715968264381621468592963
8952175999932299156089414639761565182862536979208272237582511852
10916864000000000000000000000000
  • 프로그래밍 언어에서 제공하는 자릿수 제한 없는 곱셈을 이용하거나, 이런 형태의 곱셈 함수를 직접 구현해도 답을 얻을 수 있지만, 문제의 의도와는 다릅니다.

  • 정답 검토의 편의를 위해 블로그 포스팅에 2012!와 10000!의 결과를 남겨주세요.

  • (심화 문제) 연속된 0 앞에 나오는 여러 숫자를 구하는 것도 가능하니, 심심하신 분은 도전해보세요. ^^

풀이

먼저 입력값이 2012와 10000일 때의 결과는 아래와 같습니다.

input : 2012

output: 501 8


input : 10000

output: 2499 8

제가 푼 방식은 10은 5와 2의 배수라는 점을 이용했습니다. 곱해지는 숫자를 2와 5로만 소인수 분해하고 그 갯수를 각각 누적해서 구합니다. 즉 최종 팩토리안 값의 약수 중에서 2와 5가 몇 번 들어가 있는지를 계산하는 것이죠. 그리고 2,5가 아닌 나머지 약수는 구하고자하는 자릿수만큼만 잘라서 보관합니다.

예를 들면 5!에서 0의 갯수가 0이 아닌 마지막 숫자를 구한다면

5! = 1 * 2 * 3 * 4 *5 = 2^3 * 5 * 3

이이므로 2는 3번, 5는 1번 들어갑니다. 0의 갯수는 2,5가 짝이 맞는 갯수이므로 둘 중에 작은 숫자인 5의 약수의 갯수, 즉 1개입니다.

그리고 마지막 0이 아닌 숫자는 나머지 약수의 마지막 숫자인 3과 5와 짝이 맺어지지 못한 2의 배수를 곱해서 구합니다.

여기서는 약수인 2중 1개는 5와 짝이 맺어졌고, 나머지 2개가 남았으므로 2^2*3 = 12, 마지막 숫자만 남기면 2입니다.

테스트 코드에서는 이미 알려진 숫자를 검증했습니다.

public class FactorianAnalyzerTest {
    FactorianAnalyzer analyzer = new FactorianAnalyzer();

    @Test

    public void test5(){
        assertFactorianResult(5, 1, 2);
    }

    @Test
    public void test15(){
        assertFactorianResult(15, 3, 8);
    }

    @Test
    public void test20(){
        assertFactorianResult(20, 4, 4);
    }

    @Test
    public void test30(){
        assertFactorianResult(30, 7, 8);
    }

    @Test
    public void test40(){
        assertFactorianResult(40, 9, 2);
    }

    @Test
    public void test50(){
        assertFactorianResult(50, 12, 2);
    }

    @Test
    public void test100(){
        assertFactorianResult(100, 24, 4);
    }

그리고 꼭 마지막 1개의 숫자가 아닌 여러 숫자로 구할 수 있도록 확장했기 때문에 그것도 검증해보았습니다.

public class FactorianAnalyzerNonZeroTwoDigitsTest {

    FactorianAnalyzer analyzer = new FactorianAnalyzer();

    @Test
    public void test5(){
        assertFactorianResult(5, 2, 1, 12);
    }

    @Test
    public void test6(){
        assertFactorianResult(6, 2, 1, 72);
    }

    @Test
    public void test7(){
        assertFactorianResult(7, 3, 1, 504);
    }

    @Test
    public void test8(){
        assertFactorianResult(8, 2, 1, 32);
    }

    private void assertFactorianResult(int n,  int numOfLastNonZeroDigits, int expectedZeroCount, int expectedNonzeroDigit) {
        FactorianResult result = analyzer.countZero(n, numOfLastNonZeroDigits);
        assertThat(result.getZeroCount(), is(expectedZeroCount));
        assertThat(result.getNonZeroDigits(), is(expectedNonzeroDigit));
    }
}

전체 코드는 아래 주소에 올렸습니다.