2007. 11. 27. 22:11

새로운 웹RSS리더 웹피쉬

나의 기본 RSS리더는 Google Reader이다. 몇몇 rss프로그램도 써봤지만 웹RSS리더의 접근성을 포기하게 만드는 프로그램은 보지 못했다. 한RSS리더도 써봤지만 직관성이나 속도가 Google Reader이 더 빠르게 느껴졌기 때문에 현재는 google reader에 안착해 있다. 물론 내가 gmail을 사용한다는 것도 중요한 이유로 작용했다.

이번에 새로운 웹RSS리더 웹피쉬가 나왔다는 얘기를 듣고 방문해봤다. 간단한 리뷰를 쓰면 커피교환권을 준다는 유혹에 넘어가 잠깐 사용해 보게 되었다.
길게 사용해볼 것도 없이 나의 판단은 내려졌다.
화면은 수려하고 기능은 우수하다 그러나 네비게이션이 불편하다.
최근 web2.0의 경향을 따라 매우 다양한 기능과 아름다운 화면을 가지고 있다. 클릭을 하지 않아도 팝업 메뉴가 뜨기 때문에 클릭 횟수를 줄일 수도 있다.
그러나 결정적으로 구글리더에 비해 네비게이션이 불편하다. 글을 읽으면서 구글리더에서 j,k나 방향키를 이용해 글 사이를 이동하던 것이 그리워진다. 무엇보다 직관적이지 않다. 제목을 클릭해서 글을 펼치는 것이라는 것은 쉽게 알 수 있었는데 이 후에 어떻게 접어야 할지 모르겠다.  물론 베타이기 때문에  차후에 보다 사용자 편의가 고려될것이다. 지금의 디자인과 기능에 사용자 편의성이 고려된다면 충분히 매력적인 웹RSS가 될것이다. 물론 아직 나는 구글 리더가 좋다.

'Web' 카테고리의 다른 글

Slide.com  (0) 2007.11.04
2007. 11. 19. 13:53

map, grep 그리고 //g의 함정

문제는 펄스터디 모임중 다음 코드에서 시작되었다.
#!/usr/bin/perl
@str = qw(test test1 test28 test88 test102 test9209);
@grep_ret = grep m/test[0-9]{2}/g, @str;
print("grep_ret : @grep_ret\n");

@str = qw(test test1 test28 test88 test102 test9209);
@map_ret = map m/test[0-9]{2}/g, @str;
print("map_ret : @map_ret\n");
결과 :
grep_ret : test28 test88 test102 test9209
map_ret : test28 test88 test10 test92


이 코드는  map과 grep의 차이를 잘 보여주는 코드이다. 그런데 문제는 두번째 @str을 주석처리 하자 map의 결과가 사라졌다는 것이다.
#!/usr/bin/perl
@str = qw(test test1 test28 test88 test102 test9209);
@grep_ret = grep m/test[0-9]{2}/g, @str;
print("grep_ret : @grep_ret\n");
#@str = qw(test test1 test28 test88 test102 test9209);
@map_ret = map m/test[0-9]{2}/g, @str;
print("map_ret : @map_ret\n");
결과 :
grep_ret : test28 test88 test102 test9209
map_ret :
처음에는 매우 의아한 결과라고 생각했지만 찬찬히 살펴보자 //g의 문제라는 것을 알았다. 첫 grep 에서 g에의해서  매치 포지션이 뒤로 이동해 버린 것이 문제였다.  이를 증명 및 해결하기 위해서 다음 코드를 사용했다.

#!/usr/bin/perl
@str = qw(test test1 test28 test88 test102 test9209);
@grep_ret = grep m/test[0-9]{2}/g, @str;
print("grep_ret : @grep_ret\n");
map{pos=0}@str;
@map_ret = map m/test[0-9]{2}/g, @str;
print("map_ret : @map_ret\n");
결과 :
grep_ret : test28 test88 test102 test9209
map_ret : test28 test88 test10 test92
pos를 이용해서 매치 포지션을 0으로 바꾸자 우리가 원하던대로 코드가 작동했다.
이에 대해 몇가지 얘기들이 있었고 본인이 잘못알고 있던 것을 아는 척하다가(grep에서 반환값이 리스트라는. 엉터리 정보를 주장했다.) grep과 map의 순서를 바꾸자 더 황당한 일이 벌어졌다.
#!/usr/bin/perl
@str = qw(test test1 test28 test88 test102 test9209);
        @map_ret = map m/test[0-9]{2}/g, @str;
print("map_ret : @map_ret\n");
  @grep_ret = grep m/test[0-9]{2}/g, @str;
 print("grep_ret : @grep_ret\n");
 @map_ret = map m/test[0-9]{2}/g, @str;
 print("map_ret : @map_ret\n");
결과 :
map_ret : test28 test88 test10 test92
grep_ret : test28 test88 test102 test9209
map_ret :
이럴수가, 왜 map은 grep에 영향을 주지 않았는데 grep는 map에 영향을 주는 걸까.

이제 문제를 일으킨 //g와 map, grep의 특성들을 자세히 살펴보자.
//g 는 하나의 문자열에 대해서 여러번(global) 매치를 실행하기 때문에 매치가 이루어질때마다 매치가 이루어진 다음의 위치를 메모리에 저장한다. 이 위치는 pos함수로 확인할 수 있고 또 pos에 값을 대입해서 이 위치를 수정할 수 있다. 그런데 매치를 다 찾아서 더이상 매치가 없는 문자열에 다사  //g를 적용하면 어떻게 될까. 예를 통해 확인해보자.
#!/usr/bin/perl
use strict;
my $str="test12test34";
my $count;
$count = $str=~/test/g; print $count, "\t", pos $str, "\n";
$count = $str=~/test/g; print $count, "\t", pos $str, "\n";
$count = $str=~/test/g; print $count, "\t", pos $str, "\n";
$count = $str=~/test/g; print $count, "\t", pos $str, "\n";

결과:
1       4          # 매치가 되었다는 뜻의 1과 다음 매치위치인 "1"의 위치 4가 출력되었다.
1       10        # 마찬가지로 1과 다음 매치위치인 "3"의 위치인 10이 출력되었다.
                    # 더이상 매치할 것이 없기때문에 실패의 의미인  undef가 출력되었고 pos역시 undef.
1       4          # 바로 이거다. 한번더 //g가 호출되면 처음부터 다시 매치를 시작한다.

//g를 리스트에 저장하면 어떻게 될까.
#!/usr/bin/perl
use strict;
my $str="test12test34";
my @result;
@result = $str=~/test/g; print @result, "\t", pos $str, "\n";
@result = $str=~/test/g; print @result, "\t", pos $str, "\n";

결과
testtest
testtest

//g는 모든 매치를 실행해서 매치된 내용을 @list에 집어넣었고 매치가 모두 이루어졌기 때문에 pos는 undef를 반환했다. 앞의 결과와 비슷하게  pos가 undef가 된후 다시 매치를 실행하자 처음부터 매치가 이루어진다.
그렇다면 앞의 코드를 다시 인용해 모든 문제를 짚어보자.

#!/usr/bin/perl
@str = qw(test test1 test28 test88 test102 test9209);
        @map_ret = map m/test[0-9]{2}/g, @str;
print("map_ret : @map_ret\n");
  @grep_ret = grep m/test[0-9]{2}/g, @str;
 print("grep_ret : @grep_ret\n");
 @map_ret = map m/test[0-9]{2}/g, @str;
 print("map_ret : @map_ret\n");
결과 :
map_ret : test28 test88 test10 test92
grep_ret : test28 test88 test102 test9209
map_ret :
첫번째  map에서 map은 list를 반환한다. 따라서 //g는 리스트 컨텍스트로 작동하고 모든 매치된 내용을 리턴 한 후에 매치 포지션을 초기화 시킨다. 따라서 grep은 아무 문제없이 작동한다. 하지만 grep은 스칼라 데이터를 반환하기 때문에 매치 위치를 뒤로 옮겨버립니다.(사실 이말은 틀린 말이네요. grep의 반환값이 아니라 grep이 입력된 리스트 요소들에 대해서  True/False를  판단하기 위해서  마지막  expression의  스칼라 값을  사용한다는 것이 맞겠지요. 예를 들어 극단적으로 grep{(1,2,3,0)}@InputLIst 의 경우에 첫번째 스터디에서 공부했던 것 처럼 (1,2,3,0) 의 마지막 값인 0을 사용해 모두 False라고 판단하게 됩니다.) 따라서 마지막 map에서는 이동된 매치 포지션을 가지고 매치를 실행하게 되고 더이상 매치 할 것이 없으므로 undef만 줄기차게 반환하게 되겠지요. 다음 예제를 보면 좀더 분명해 지리라 봅니다. 매치의 정규표현식과 데이터를 살짝 바꿔봤습니다.
#!/usr/bin/perl
@str = qw(1and2 3and4and5 6 7 8 9 nodigit);
        @map_ret = map m/\d/g, @str;
print("map_ret : @map_ret\n");
  @grep_ret = grep m/\d/g, @str;
 print("grep_ret : @grep_ret\n");
 @map_ret = map m/\d/g, @str;
 print("map_ret : @map_ret\n");
결과 :
map_ret : 1 2 3 4 5 6 7 8 9
grep_ret : 1and2 3and4and5 6 7 8 9
map_ret : 2 4 5
두번째 grep에서 매치 위치를 옮긴 후에도 매치될 내용이 남아 있는 요소 에 대해서는 마지막 map에서 모두 매치해서 반환해 버립니다.

몇가지 요소가 복합적으로 얽혀있는데다가 쭈루룩 쓴 글이라 좀 복잡하네요.
요약하자면
1. //g는 매치가 이루어질때마다 다음 매치를 위해 매치위치를 옮긴다.
2. 매치 위치는 pos를 이용해 알아내거나 수정할 수 있다.
3. 스칼라문맥에서 //g는 호출될때마다 매치성공여부를 반환하고 매치위치를 옮긴다. 매치가 다 이루어진 후에는 //g가 한번더 호출되면 undef를 반환하고 매치 위치를 undef로 설정한다(매치 위치가 문자열의 처음으로 재설정 되는 것과 같은 효과). 따라서 다음 //g 는 문자열의 처음부터 다시 시작된다.
4. 리스트 문맥에서 //g는 가능한 모든 매치를 실행하여 매치된 값들의 리스트를 반환한다. 그리고 매치 위치도 undef로 바꾼다. 따라서 리스트 문맥 이후에 //g는 문자열의 처음부터 매치를 시작한다.
5. grep에서 //g 스칼라 문맥이고 map에서 //g는 리스트 문맥이다. 따라서 grep에서는 매치 위치가 바뀌에 되고 map이후에는 매치위치가 문자열의 처음이 된다.

끝입니다.^^

---
글을 올리고 나서 너무 미진한 부분이 많이 보여서 수정및 추가를 해야하나 하고 고민하고 있었는데 마침 aero님께서 제 고민을 한방에 날려주셨습니다. 감사. 역시 고수의 글은 무섭군요!!





'PerlMania' 카테고리의 다른 글

3번째 펄마니아 스터디 모임  (5) 2007.11.18
펄마니아 스터디 후기  (9) 2007.11.04
2007. 11. 18. 16:35

3번째 펄마니아 스터디 모임

이번주 스터디도 연대에서 있었습니다. 아침부터 지갑을 잃어버리는 등의 악재가 겹쳐서 4시에 딱 맞춰 가는 바람에 범규가 혼자 고생이 많았네요.
예상대로(?) 뜨거운 스터디 시간이었습니다. 4시 30분경에 시작해서 9시 30분까지 5분쉬고 다섯 시간을... 술자리를 위해서 말리고 싶은 마음도 있었지만 발표하시는 분들의 열기에 말도 꺼낼 수가 없었습니다.(사실은 저의 소심함이 가장 큰 원인이었죠.)
지난 시간부터 문제가 되었던 grep,map 문제는 제가 정확히 모르고 떠벌이다가 창피를 당하기도 했습니다.
그러나 기대하시라.. 해결했습니다. 관련 포스트를 따로 올리기로 하지요. 포인트는  grep의 반환값은 스칼라이고 map의 반환값은 리스트라는 것입니다.

스터디가 끝나고 지지난 주부터  계획만하던 "스터디 회원 사진찍기"를 마침내 실행에 옮겼습니다.
신비주의를 원하시는 분들이 있을까봐 걱정했었는데 다행히 모두들 찬성하시더군요.
아쉽게도 이번에 참석하지 못하신 혁진님과 성일님(맞나요?)께서는 다음주를 기약하셔야 겠습니다.
사진은 왼쪽 위에 키가 제일 크신 분부터 시계방향으로 박민영님, 황찬님(고기스님), 이성찬님, 김범규님(bkkim), 임도형님, 저 장범수(pung96), 정현기님(simryang), 김대호님, 김도형님(keedi) 입니다.
임도형님은 이번에 처음 참석하셨는데 내공이 남다르시더군요. 특히 사근사근 설명해 주시는것이 전직이 의심스워지네요(선생님?^^).
사용자 삽입 이미지

펄마니아 스터디 사진


아쉽게도 너무 늦게 끝난데다가 다들 급한 일이 있으셔서 뒷풀이는 저, 김도형님, 김대호님, 이성찬님, 황찬님 이렇게 5명이 진행했습니다. 주종목은.. 역시 제가 좋아하는 삼겹살에 소주였습니다. 도형님은 자전거를 타고 오신 관계로 맥주만 드셨습니다. 막판에 결심을 바꾸시고 소주로 바꾸시려고 했는데 제가 술취하면 말이 많아지는 관계로 이런 저런 얘기에 열을 올리느라 신경을 못써드려서 그냥 맥주만 드셨네요.
무사히 보내 드려서 죄송합니다.
다음번에는 꼭 소주를 열심히 권해드리겠습니다.용인까지 자전거 타고 가신다던데..  대단하십니다.
저는 다른 모임이 있어서 2차를 갔다가 너무 취해 버리는 바람에 쓰러지기 일보직전에 가방을 움켜쥐고 인사도 없이 집으로 뛰어갔다가 선배들에게 부지런히 욕먹는 중입니다.
다들 수고하셨습니다.

'PerlMania' 카테고리의 다른 글

map, grep 그리고 //g의 함정  (3) 2007.11.19
펄마니아 스터디 후기  (9) 2007.11.04
2007. 11. 6. 15:02

리스트 비교

리스트를 비교하는 방법에는 "grep -f", "sort, diff", "comm" 등이 대표적이다.
"grep -f" 는 시간이 너무 오래 걸리는 단점이 있지만 diff, comm등은 강력한 도구이다.
하지막 역시 많은 파일과 복잡한 선택기준 앞에서 나의 가장 강력한 도구는 PERL!!

내가 주로 쓰는 커맨드를 소개한다면
perl -nle '/(regex)/ and $h[@ARGV]{$1}=$_;END{map{statement;}keys %{$h[0]}}' file1 file0
주의 할 점은 @ARGV(남은 파일 개수)을 인덱스로 사용하기 때문에 인자로 준 파일순서와 인덱스가 거꾸로라는 것.
위 예제에서 file1이 $h[1], file0 가 $h[0] 에 저장된다.(file1 과 file0의 순서에 주의)
statement를 위해서 여러가지 상상력을 동원할 수 있다.

간단한 예
perl -nle '/(\d{6}-\d{4})/ and $h[@ARGV]{$1}=$_;END{map{ exists $h[1]{$_} and print $h[1]{$_} }keys %{$h[0]}}' file1 file0

file1과 file0에서 123456-1234형태의 패턴(내가 주로 사용하는 런넘버-세그먼트넘버) 을 추출해서 file1과 file0의 공집합의 file1리스트를 출력한다.
비교할 리스트의 성질에 따라 코드는 많이 짧아질 수도 있다.
극단적으로 두 리스트의 똑같은 라인들만 검색하기 위해서는 다음과 같이 할 수도 있다.

perl -e '@s{`cat OLD`}=( ); exists $s{$_} && print for `cat NEW`' fire1 file2
2007. 11. 6. 10:26

터미널의 타이핑 내용을 녹화한다.

가끔 심심할때면 쉘에서 특정 영단어로 시작하는 프로그램들이 어떤것이 있는지 찾아보곤 하는데 얼마전 script 란 주옥같은 명령어를 찾게 되었다. 우왓 내가 모르던 스크립팅 언어인가!!

 script - make typescript of terminal session

말 그대로 작업내용을 녹화해 놓았다가 재생할 수 있다.
  • 녹화
$ script -t 2> timing
        Script started, file is typescript
        $ ls
        Desktop         hax             hog.sh  My Computer             ostg    src
        $ pwd
        /home/jonesy
        $ file hax
        hax: empty
        $ exit
        exit
        Script done, file is typescript

  •  재생
        $ scriptreplay timing
        $ ls
        Desktop         hax             hog.sh  My Computer             ostg    src
        $ pwd
        /home/jonesy
        $ file hax
        hax: empty
        $ exit
        exit


작업하는 모습을 다른 사람에게 실시간으로 보여줄수도 있다.
선생님 :
  $ mkfifo  out 
  $ script -f out

학생 :
 $ cat out

선생님:
 $ 이렇게 이렇게 하는 거예요..
 $ exit

오~~ 멋지다.. 그런데 쓸일은 별로 없을 것 같네..
참고로 screen 으로도 실시간 교육을 할수가 있다는 사실.. 역시 별로 쓸일은 없지만.
2007. 11. 5. 20:23

한꺼번에 많은 파일 지우는 세가지 방법

원래 펄 사용의 주 목적이 리스트 및 파일 관리였기 때문에 다양한 요구조건에 대해서 가장 효율 적인 방법은 일일이 스크립트파일을 만드는 것 보다 awk, 나 sed 처럼 커맨드라인에서 바로 코드를 만드는 방법이라고 생각하고 있었다.  덕분에 프로그램을 만들때 조금만 길이기 길어져도 헤매는 부작용이 생기기는 했지만.

그 전까지는 그냥 상상력에 의존하여 나름 필요한 코드를 만들어오곤 했었는데 다음 코드를 보고는 그 포스에 감동해버렸다.
find ./ -name "*.bak" -type f| perl -nleunlink  # 코드 1
현재 디렉토리 아래의 모든 *.bak파일을 지우는 코드.

한 디렉토리에 지워야 할 파일이 매우 많을때(1024개 이상?)
rm -f *.bak        # 코드 2
는 작동하지 않는다. 이때 위의 perl 코드1은 잘 동작한다.(물론 코드1은 하위디렉토리까지 검색한다는 차이는 있다.)
 perl이외에도 방법은 있다.
find ./ -name "*.bak" -type f -exec rm -f \{\} \;       #코드3
find ./ -name "*.bak" -type f | xargs rm -f                   #코드4
코드3의 문제는 모든 파일마다 rm을 실행하므로 매우 느리다는 데 있다.
코드4의 문제는 xargs를 이용하기 때문에 rm을 호출하는 횟수가 더 적어진다.
코드1의 경우 단 하나의 perl 프로세스를 이용하여 파일을 지우기 때문에 이방법이 가장 빠를 것으로 예상할 수있다.

사실 코드4의 xargs 가  코드1의 perl보다 그리 느릴꺼라고 생각하지는 않는다. 적어도 한 프로세스로 100개이상의 파일을 처리할 수 있다면 소모되는 시간은 파일을 삭제 하는데 걸릴 시간이기 때문이다. 게다가 코드1은 사실상 perl의 아름다움을 보여주기 위하여 좀 과장된 측면이 있다. 예를 들어 처리중에 지우지 못하는 파일이 있어도, 혹은 아예 없는 파일이 리스트에 주어져도 아무런 경고를 내지 않는다. 이를 해결하기 위해서 우리는 "die" 나 "print" 또는 "warn"등의 구문을 추가해야한다.
find ./ -name "*.bak" -type f | perl -nle'unlink or die"Can't remove $_ : $!"'
빠른 방법이라고는 하지만 이제 코드쓰기가 매우 귀찮아졌다. 나라도 xargs가 쓰고싶어 질 것이다. (실제로 그러고 있다.) 하지만 반대로 생각해볼 수 있다. 지우지 못하는 파일에대한 경고를 추가했던 것처럼 우리는 필요한 무엇이든 추가할 수 있다. 텍스트 파일만 지워보자.
find ./ -name "*.bak" -type f | perl -nle'-T and unlink or print "Can't remove $_ : $!"
 find 의 옵션들을 perl로 옮겨 더욱 강력하게 만들 수도 있다.
find | perl -nle '/\.bak$/ && -f && -T and unlink or print "Can't remove $_ : $!"
(파일 테스트 연산자가 제대로 맞는지 잘 기억이...)
(주의: && and 등의 연산자가 헷갈린다면 언제나 괄호와 if 가 우리 앞에 있음을 있지 말자)

정말 지워야할 파일이 많을 수록. 지워야 할 파일을 선택하는데 기준이 복잡할 수록, 위의 코드 한줄이 점점 짧게 보일 것이다.

'Perl Recipe' 카테고리의 다른 글

두 문자열에서 중복되는 부분 찾기  (0) 2009.03.25
일전한 글자수의 단어 세기  (7) 2009.03.24
원라인 펄 놀이 - 첫줄 빼고 sort  (0) 2008.04.17
여러파일을 sorting 하기  (0) 2007.12.11
리스트 비교  (0) 2007.11.06
2007. 11. 4. 21:29

Slide.com

IMPRESS MAGAZiNE에서 보고 slide.com에서 슬아이드를 만들어보았다.
슬라이드 기능은 매우 훌륭하고 한번에 여러장의 사진을 올릴 수도 있다. 어떤 방법을 쓴거지?
사진수 제한이 없기는 하지만 여러장의 사진을 한꺼번에 다운받는다던가 하는 기능이 친절하지 않아 사진 보관용으로는 훌륭한 것 같지는 않다. 
아직은 picasa와 연동가능한 google webalbum이 사진보관용으로는 제일 적당한 것 같다.
google webalbum이 용량제한이 있기는 하지만 여러 계정을 사용하면.. 웁. 이거 말해도 되는건가?


'Web' 카테고리의 다른 글

새로운 웹RSS리더 웹피쉬  (0) 2007.11.27
2007. 11. 4. 15:40

펄마니아 스터디 후기

어제(10월3일) 펄마니아 스터디가 있었습니다.
권혁진님, 김대호님, 김도형님, 박민영님, 저, 제 후배 2명, 그리고 한분(이름이 잘 생각이...) 이렇게 8분이 오셨습니다. 맞나요?  카메라를 가져가지 않아서 사진은 하나도 못찍었네요.

모임 장소인 숭실대가 신촌에서 가까울 거라고 마음대로 상상해 버렸다가 모임시간이 3시에서 20분이나 늦게 도착했습니다. 게다가 "웨스터민스터 관" 을 찾느라고 이리 저리 해매고, 다른 건물에 올라갔다가 다시 내려오는 일을 반복하느라 굉장히 늦어버렸는데 다행히(?) 세미나실 빌리는 문제로 모임이 한시간 가량 지체되어서 마음속으로 만들었던 수많은 변명들을 하지 않아도 되었습니다.
모임장소 확보하시느라 고생하신 도형님께 박수를!!

"펄 제대로 배우기 2판"이 교재여서 금방 스터디가 끝날 줄 알았는데 참석자 분들의 뜨거운 열의로 결국 4챕터를 진행하는데 3시간이 넘는 시간이 걸렸죠.  특히 도형님과  혁진님의 물흐르는 듯한  친절하고 자세한 설명은  매우 감동적이었습니다.

스터디가 다 끝나고 나서 다음 스터디 계획을 좀 세우고 나서는 헤어졌습니다.
원래 첫 스터디고 해서 술 한잔 하자고 말하려고 했는데 도형님께서 먼저 "책 한권 끝날때 마다 책거리 하자"고 말씀하시는 통해 원래 소심함 pung96은 쑥 들어가 버렸습니다.
오면서 후회도 되고 해서.. 그냥 신촌에서 후배 둘과 함께 삼겹살을...

펄 제대로 배우기 교재를 쓴다고 해서 재밌을까 걱정도 되었었는데 생각보다 훨씬 재미있었습니다.
어제 정말 재대로 배운것이 두가지가 있습니다.
    1. 역시 공부는 혼자해서는 한계가 있다.
    2. 아.. 러닝펄에서 내가 모르는게 이렇게 많았구나.



'PerlMania' 카테고리의 다른 글

map, grep 그리고 //g의 함정  (3) 2007.11.19
3번째 펄마니아 스터디 모임  (5) 2007.11.18