IBM®
메인 컨텐츠로 가기
    Korea [국가변경]    이용약관
 
 
   
        제품    서비스 & 솔루션    고객지원 & 다운로드    회원 서비스    
메인 컨텐츠로 가기

한국 developerWorks  >  리눅스  >

"JAPH"

Just Another Perl Hacker

developerWorks
문서 옵션

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.


난이도 : 초급

Teodor Zlatanov, 프로그래머, Gold Software Systems

2001 년 7 월 01 일

JAPH는 "Just another Perl hacker"를 만들어내는 짧은 스크립트이다. 초급 또는 중급의 Perl 프로그래머를 위해 이 글을 썼지만, 이 글에 쓰인 간단한 JAPH 예제는 수준 높은 전문가에게도 도움이 될 것이다.

JAPH는 4행의 코드 (라인 당 80 문자 이하)에서 "Just another Perl hacker" 스트링을 만들어 낸다. 그래서 JAPH는 USENET signature에 알맞다. USENET signature는 Perl 보다 훨씬 오래되었다.

JAPH: "Just another Perl hacker"

JAPH format은 1990년대 초에 Randal Schwartz 에 의해 대중화되었다. 오늘날 JAPH는 뉴스그룹의 Abigail 같은 "지치지 않는 예술가"에 의해서 만들어지고 있다.

이제부터 몇 가지 JAPH를 연구하려고 한다. CPAN (참고자료)의 표준 리스트에서 참고했다. 다양한 테크닉도 설명하겠다. Programming Perl, 3rd Edition (참고자료)에서 자세한 내용을 참고하기 바란다.

예제를 정확히 따라가기 위해서 Perl 5.6.0 을 설치하도록 하자. 최신 (2000 또는 그 이후) UNIX 계열 (Linux, Solaris, BSD) 시스템이라면 더욱 좋겠다. 초기 버전의 Perl과 UNIX와 다른 OS에서 작동시켜도 큰 문제는 없지만 문제에 대한 솔루션도 함께 생각해야 할 것이다. 이 글에 나와있는 모든 JAPH는 표준 리스트 포맷이며 날짜와 작성자 attribution도 함께 제공한다.




위로


"좋은" JAPH

본격적으로 시작하기 전에, JAPH 초기에 Randal Schwartz가 작성한 네 개의 재미있고 쉬운 코드를 살펴보겠다. 아래의 첫 번째 예제는 모든 JAPH가 모호할 필요가 없다는 것을 증명한다. 심지어 판독하기도 쉽다.


Listing 1. true conditional

Date:         18 Jun 90 15:53:11 GMT
From:         merlyn@iwarp.intel.com (Randal Schwartz)
print "Just another Perl hacker," if "you can't think of anything better..."

Listing 1에서 if() 절은 항상 true 값으로 나온다. 왜냐하면 스트링이 비어있지 않기 때문이다. (빈 "" 스트링 , "0" 스트링, 0 또는 이에 해당하는 숫자, undef는 false로 나온다). 따라서 print 문장은 항상 값이 구해진다.


Listing 2. Printf 사용하기

Date:         15 Jun 90 22:06:24 GMT
From:         merlyn@iwarp.intel.com (Randal Schwartz)
printf "%s %s %s %s%c", 'Just', 'another', 'Perl', 'hacker', 44

Listing 2는 또 다른 초기 예제이다. printf() 함수를 사용하여 원하는 아웃풋을 만들어낸다. 원한다면 C 처럼 보이도록 만들수도 있다.

Schwartz는 Listing 3에서 약간 복잡한 기술을 사용한다. print()에 재 배열한 어레이를 주고 단어 사이에 공간을 주어 프린트하도록 했다 ($,는 한 번에 프린트할 때 어레이 엘리먼트들 사이에 넣을 것을 Perl에게 명령하는 변수이다).


Listing 3. 어레이 재 배열

Date:         5 Jun 90 19:07:58 GMT
From:         merlyn@iwarp.intel.com (Randal Schwartz)
$,=" "; print +("hacker,","Just","Perl","another")[1,3,2,0];

어레이 앞에있는 + 는 print()가 다음에 뒤따르는 것을 단일 매개변수로 취급 할 수 있도록 한다. (이 경우, 어레이는 괄호안에 있다). 함수 호출을 의미하는 괄호를 하지 않는다. 다음과 같이 하지 않도록 한다; print ('a', 'b')[1]; print()가 'a' 와 'b'를 각각 첫 번째와 두 번째 매개변수로 취해서 프린트 된다. 그리고 Perl은 [1]로 어떤 것을 해야하는지 인식하지 못한다.

Listing 4는 가장 오래된 JAPH 중 하나이다. split(), sort(), grep()을 이용한다:


Listing 4. Sort then grep

Date:         6 Feb 90 22:31:17 GMT
From:         merlyn@iwarp.intel.com (Randal Schwartz)
print grep(s/^\d+(.*)/$1 /, sort(split(/ /,"8hacker, 4Perl 1Just 2another")));

우선, 시작 스트링을 네 개의 엘리먼트로 쪼갠다: "8hacker," "4Perl" "1Just" "2another".
그런 다음 영숫자로 (alphanumerically) 'sort' 한다: "1Just" "2another" "4Perl" "8hacker,".

"10Just""8hacker" 앞으로 가야 한다는 것에 주목하라. 이것은 숫자 소팅이 아니다.

소팅된 리스트는 grep()으로 이동된다. 앞의 모든 숫자를 없애고 남겨진 것에 공간을 붙인다. 결과는 "Just " "another " "Perl " "hacker, "이다.

마지막으로, print()는 리스트에 호출되어 문자로 프린트된다.




위로


"나쁜" JAPH

이제 "나쁜" JAPH를 살펴보도록 하자. "좋은" JAPH는 부드럽게 설명했지만, "나쁜" JAPH는 여러분의 머리를 괴롭힐 것이다. JAPH가 만들어낸 것을 10분 정도 보고 있으면 이것이 "나쁜" 것이라는 것을 알게 될 것이다.


Listing 5. Substitution and eval

Date:         26 Mar 90 16:20:37 GMT
From:         raymond@sunkist.berkeley.edu (Raymond Chen)
$_='x"Not ";"x\"another \";\'x\\"perl \\";x\\"hacker,\\"\'"';s/x/print/g;eval eval eval;

스트링은 single quote로 시작해서 single quote로 끝나야 한다. 위에서 single quote들을 찾아 보면 두 개가 없어지고 (역 슬래쉬와 함께), 세 번째 것은 원래의 것임을 알 수 있다.

이제 치환을 실행시켜보자 (s/// 명령어 다음에 print() 를 넣어 결과를 보자): print"Not ";"print\"another \";'print\"perl \";print\"hacker,\"'"

그리고 명령어가 보인다. 왜, 한 개가 아닌 세 개의 eval() 명령어를 실행시키는가? 자세히 살펴보자. 두 번째 print() 는 스트링 안에 있다. 그래서 첫 번째 eval()로 계산되지 않는다. 하지만 첫 번째 eval()은 계산된 첫 번째 레벨 스트링을 리턴한다: print"another ";'print"perl ";print"hacker,"'. 그리고 "Not"을 프린트 할 것이다. 왜 스트링의 첫 번째 부분이 첫 번째 eval()에 의해 리턴되지 않는가? eval()은 계산된 마지막 것만을 리턴하기 때문이다. 마지막 문장을 "eval eval eval" 대신 "print eval"로 만들어서 어떻게 작동되는지 보자.

두 번째 eval은 무엇을 하는가? 이것은 두 번째 것을 계산하지만 세 번째 또는 네 번째 print() 문장은 계산하지 않는다. 두 개 모두 single-quote 스트링 안에 있다는 것을 알게 될 것이다. 두 번째 eval은 세 번째와 네 번째 print() 문장을 그 안에 포함시켜서 스트링을 리턴할 것이다. 그리고 "another "라고 프린트 된 것을 남겨놓는다. 두 번째 eval은 다음과 같이 리턴할 것이다: print"perl ";print"hacker,"

마지막으로 세 번째 eval은 그러한 두개의 print() 문장을 실행하여 JAPH를 완성할 것이다. (예측불허의 "Not another perl hacker,"를 프린트한다).

"나쁜" JAPH은 해결할 수가 있다. 심지어 단순히 디코딩 하는 것 처럼 간단한 어떤것이 여러 단계의 복잡성을 가진 것으로 판명날 수 있다.

다른 "나쁜" JAPH 예제를 보자:


Listing 6. Abigail's jawbreaker

#Abigail
$_ = "\x3C\x3C\x45\x4F\x54"; s/<<EOT/<<EOT/e; print;
Just another Perl Hacker
EOT

Listing 6 또한 간단한 JAPH 처럼 보인다. 위 스트링의 비밀은 무엇인가? Abigail 스타일은 여러분이 전에 보았던 것을 새로운 방식으로 사용하는 것이다. 예를 들어, s/// 연산자는 "e" modifier에 주어진다. 치환하기 전에 오른쪽의 수식을 계산하도록 한다. 그래서 "<<EOT" 는 다음 라인 부터 EOT까지 모든 것과 대체된다. 이 경우 , "Just another Perl Hacker"가 된다.

코딩 된 $_ 스트링은 "<<EOT"를 포함한다. 그래서 s/// 연산자에 의한 치환은 "<<EOT" 스트링을 "<<EOT"의 계산 결과로 바꾸면서 끝난다. 이것이 "Just another Perl Hacker" 스트링이다. print() 문장은 그 스트링을 프린트하도록 한다.

코딩된 스트링과 간단한 치환은 JAPH의 주류를 이루고 있다. 특히 치환은 새롭고 놀라운 방식으로 만들어질 수 있다.

다른 Abigail의 예제를 보자:


Listing 7. Prototypes explained by Abigail

#Abigail
perl -wle 'sub _ "Just another Perl Hacker"; print prototype \&_'

이 JAPH를 이해하기 위해서는 prototype에 대한 실제적인 지식이 필요하다. "perldoc -f sub"과 "perldoc -f prototype" 문서가 도움이 될 것이다. 기본적으로 새로운 함수가 "_" 바디(body) 없이 "Just another Perl Hacker" prototype과 함께 만들어진다.

Programming Perl, 3rd Edition (참고자료)의 prototype과 관련한 섹션을 읽어보면 prototype은 스트링이 될 수 없다는 것을 알수 있을 것이다. Abigail은 그러한 사실을 간단히 무시하고 (그 함수는 결코 사용되지 않는다) 무효 prototype을 만든다.

올바른 것인가? 그렇다. 이것은 프로그램을 망치지 않는다. 이상한가? 약간 그렇다. 하지만 이상한 부분들이 많지만 적어도 이것은 함수가 정의되면 prototype의 적법성은 검토되지 않는다. 또한 "_" 함수를 지정할 수 있다. (일반적으로는 지정할 수 없다). 왜냐하면 이것은 빌트인(built-in) "_" 연산자와 충돌을 일으키기 때문이다.




위로


"고약한" JAPH

이제, "고약한" JAPH에 대해 살펴보자. 말 그대로 고약하고 많은 수작업이 필요하다.

다음은 Kickstart가 작성한 것이다. "고약한" 이유가 하나 뿐만은 아니다. 4줄 제한은 이미 사라졌다.


Listing 8. Hearts afire

#Kickstart from http://www.perlmonks.com/
#note: a slight valentine variation :)

      $LOVE=               AMOUR.
    true.cards.        ecstacy.crush 
  .hon.promise.de    .votion.partners.
 tender.truelovers. treasure.affection.
devotion.care.woo.baby.ardor.romancing.
enthusiasm.fealty.fondness.turtledoves.
lovers.sentiment.worship.sweetling.pure
attachment.flowers.roses.promise.poem;
 $LOVE=~ s/AMOUR/adore/g; @a=split(//,
  $LOVE); $o.= chr (ord($a[1])+6). chr
   (ord($a[3])+3). $a[16]. $a[5]. chr
    (32). $a[0]. $a[(26+2)]. $a[27].
      $a[5].$a[25]. $a[8].$a[3].chr
        (32).$a[29]. $a[8].$a[3].
          $a[62].chr(32).$a[62].
           $a[2].$a[38].$a[4].
               $a[3].'.';
                 print
                  $o; 


믿거나 말거나, 이것은 무엇인가를 수행한다. 무엇을 하는가?

변수 $LOVE는 미스테리를 푸는 첫 번째 열쇠이다. $LOVE 변수에서 모양을 만들기 위해서 스크립트를 난도질한다. 이것을 실행파일에 넣어 디버그 할 수 있다.

아웃풋은 다음과 같다(라인을 나누지 않음), $LOVE is: "AMOURtruecardsecstacycrushhonpromisedevotionpartnerstendertrueloverstreasureaffection
devotioncarewoobabyardorromancingenthusiasmfealtyfondnessturtledovesloverssentiment
worshipsweetlingpureattachmentflowersrosespromisepoem
",
Perl에서 인터프리트 된 모든 단어를 스트링으로 표현하고 있다.


Listing 9. Dump the love

#!/usr/bin/perl

use Data::Dumper;

      $LOVE=               AMOUR.
    true.cards.        ecstacy.crush 
  .hon.promise.de    .votion.partners.
 tender.truelovers. treasure.affection.
devotion.care.woo.baby.ardor.romancing.
enthusiasm.fealty.fondness.turtledoves.
lovers.sentiment.worship.sweetling.pure
attachment.flowers.roses.promise.poem;

print Dumper $LOVE;

 $LOVE=~ s/AMOUR/adore/g; @a=split(//,
  $LOVE); $o.= chr (ord($a[1])+6). chr
   (ord($a[3])+3). $a[16]. $a[5]. chr
    (32). $a[0]. $a[(26+2)]. $a[27].
      $a[5].$a[25]. $a[8].$a[3].chr
        (32).$a[29]. $a[8].$a[3].
          $a[62].chr(32).$a[62].
           $a[2].$a[38].$a[4].
               $a[3].'.';
                 print
                  $o; 



이제 "AMOUR"를 "adore"로 바꾼 다음 $LOVE를 @a라는 개별 문자 어레이로 쪼갠다. @a 의 첫 번째 엘리먼트는 "a" 이고 두 번째는 "d" 이다. 다음과 같이 만들어진다: "adoretruecards
ecstacycrushhonpromisedevotionpartnerstendertrueloverstreasureaffectiondevotion
carewoobabyardorromancingenthusiasmfealtyfondnessturtledovesloverssentimentworship
sweetlingpureattachmentflowersrosespromisepoem
"

마지막으로 @a 어레이에서 문자를 선택하여 $o 스트링을 만든다. 가끔은 좀 더 재미있게 하기 위해서 수정해야 한다.--결국 사랑이 승리한다.

다음은 구조를 해체한 스크립트 이다:


Listing 10. Love deconstructed

#!/usr/bin/perl

$LOVE = "AMOURtruecardsecstacycrushhonpromisedevotionpartners".
        "tendertrueloverstreasureaffectiondevotioncarewoobaby".
        "ardorromancingenthusiasmfealtyfondnessturtledoveslovers".
        "sentimentworshipsweetlingpureattachmentflowersroses".
        "promisepoem";

$LOVE=~ s/AMOUR/adore/g;

@a=split(//, $LOVE);

$o.= chr (ord($a[1])+6). chr (ord($a[3])+3). $a[16]. $a[5] .
#           j                   u              s       t
 chr (32). $a[0]. $a[(26+2)]. $a[27]. $a[5].$a[25]. $a[8].
# space     a          n        o        t    h       e
 $a[3].chr (32).$a[29]. $a[8].$a[3]. $a[62].chr(32).$a[62].
#  r   space      p       e     r      l    space     l
 $a[2].$a[38].$a[4]. $a[3].'.';
#  o     v      e      r

print $o; 

진실로, 사랑은 미스테리이다.




위로


결론

다양하고 재미있는 스크립트를 보았다. 다채로운 Perl 테크닉도 공부했다. JAPH는 하나의 도전이다. 여러분 스스로 직접 해보기 전에는 어떤 설명도 충분하지 않다. JAPH는 뛰어난 Perl 지식과 인내심이 수반된다는 것을 기억하라. 도전을 두려워하지 말고 참고자료 부분에 인용된 다른 JAPH를 해보기 바란다.

JAPH를 디코딩하는 것은 많은 연습을 통해 쉽게 할 수 있다. hex에 스트링을 인코딩 하거나 $, 변수를 변경하는 등의 간단한 트릭이 있다.

JAPH를 만들기 위해 시간과 노력을 아끼지 않은 많은 사람들에게 감사를 전한다. JAPH에서 새로운 것을 배울 수 있다. 내가 알고있는 한 어떤 다른 언어도 그와 같은 복잡한 형식을 가지고 있지 않다. (그것이 자랑 요소인지 부끄러워해야 할 요소인지는 모르겠다). 자세한 내용은 참고자료를 검토하라.




위로


참고자료




위로


필자소개

Teodor Zlatanov는 1999년 보스톤 대학에서 컴퓨터 공학 석사학위를 취득했다. 1992년부터 Perl, Java, C++ 프로그래머로 일하고 있다. 주요 관심 부분은 text parsing, 3-tier 클라이언트/서버 데이터베이스 아키텍처, UNIX 시스템 관리, CORBA 및 프로젝트 관리에 대한 오픈 소스 작업이다.





위로


기사에 대한 평가

매우 불만족 (1)
불만족 (2)
보통 (3)
만족 (4)
매우 만족 (5)




위로



    IBM 소개개인정보 보호정책문의