歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Unix知識 >> 關於Unix

功能豐富的Perl:JustAnotherPerlHacker

JAPH:“Just another Perl hacker” 據我們所知,JAPH 格式是在二十世紀九十年代由 Randal Schwartz 推廣的(好幾處信息來源都同意這個說法)。今天,JAPH 到處可見,它們是由該流派的那些不知疲倦的藝術家們制作的,比如 comp.lang.lang.perl.misc 新聞組
  JAPH:“Just another Perl hacker”
  據我們所知,JAPH 格式是在二十世紀九十年代由 Randal Schwartz 推廣的(好幾處信息來源都同意這個說法)。今天,JAPH 到處可見,它們是由該流派的那些不知疲倦的藝術家們制作的,比如 comp.lang.lang.perl.misc 新聞組的 Abigail。
  
  下面的討論中我們將分析 CPAN (請參閱參考資料)上的規范列表中的一些 JAPH,它們適合初級到中級 Perl 程序員。我在這裡會對各種技巧作簡單的說明,但是有興趣的讀者還是應該參閱 Programming Perl,第三版(請參閱參考資料)來進一步學習。
  
  為了精確地支持這裡所給的示例,您的系統中必須安裝有 Perl 5.6.0。最好您還安裝了最新的(2000 或者更新版本)主流 UNIX 系統(Linux、Solaris、BSD)。盡管這些示例也許能在老一點的 Perl 和 UNIX 版本甚至是其它操作系統上運行,您還是應該考慮一下如果它們運行失敗這樣需要解決的問題。每個 JAPH 都以規范列表的格式顯示,帶有一個日期和作者屬性。
  
  優良的
  在開始討論更豐富的內容之前,我們先來看看 Randal Schwartz 在 JAPH 的早些時期編寫的四段簡單有趣的代碼。我們下面的第一個示例證明了並不是所有的 JAPH 都是要晦澀難懂的,有一些甚至是很容易看懂的。
  
  清單 1. 一個簡單的 true 條件句
  
  Date:     18 Jun 90 15:53:11 GMT
  From:     [email protected] (Randal Schwartz)
  print "Just another Perl hacker," if "you can't think of anything better..."
  
  在清單 1 中,既然字符串不為空,if() 語句的值就始終為 true(只有空字符串“”、字符串“0”,數字 0 或其等價的表達形式,或者不定義時其值才為 false)。因而,將一直執行打印語句。
  
  清單 2. 使用 Printf
  
  Date:     15 Jun 90 22:06:24 GMT
  From:     [email protected] (Randal Schwartz)
  printf "%s %s %s %s%c", 'Just', 'another', 'Perl', 'hacker', 44
  
  清單 2 是另一個早期的樣本,它使用 printf() 的函數來產生所需的輸出,這也證明了如果您願意的話,Perl 看起來也可以象 C 語言一樣。
  
  在清單 3 中 Schwartz 開始玩花招了。現在我們給 print() 一個重新排列過的數組,然後將這個數組打印出來,單詞之間有空格($ 是一個變量,它告訴 Perl 在一次打印所有數組元素時在元素之間應該放什麼東西)。
  
  清單 3. 重新排列數組
  
  Date:     5 Jun 90 19:07:58 GMT
  From:     [email protected] (Randal Schwartz)
  $,=" "; print +("hacker,","Just","Perl","another")[1,3,2,0];
  
  數組前面的 + 使 print() 把緊隨其後的東西當做一個單獨的參數(在本例中因為有圓括號,所以是數組),而不是把圓括號當做是函數調用。換句話說,我們避免了以下情況: print ('a', 'b')[1]; 其中,print() 把‘a’和‘b’當做它的第一個和第二個要打印的參數,然後 Perl 就不知道 [1] 是用來做什麼的了。
  
  清單 4 是最早的有記錄可查的 JAPH,還有點別出心裁,使用了 split()、sort() 和 grep():
  
  清單 4. Sort 然後 grep
  
  Date:     6 Feb 90 22:31:17 GMT
  From:     [email protected] (Randal Schwartz)
  print grep(s/^\d+(.*)/$1 /, sort(split(/ /,"8hacker, 4Perl 1Just 2another")));
  
  首先,我們把起始字符串分割成四個元素:“8hacker,” “4Perl” “1Just” “2another”。
  然後我們排序 — 缺省情況下是按字母數字順序 — 得到: “1Just” “2another” “4Perl” “8hacker,”。
  
  注意 “10Just” 也應該排在 "8hacker" 的前面 — 這不是數字排序。
  
  排序後的列表被傳遞到 grep(),它將每個元素開頭的所有數字都去掉,並在剩下的部分後面加上一個空格。結果是: “Just ” “another ” “Perl ” “hacker, ”。
  
  最後,在這個列表上調用 print(),逐字逐元素打印。
  
  糟糕的
  看夠了 JAPH 的優點後,現在讓我們來看看它真正“糟糕到極點”的地方。良好的 JAPH 還有循序漸進的教學作用,糟糕的 JAPH 卻讓您的思維混亂得象椒鹽卷餅一樣。當您盯著一段 JAPH 冥思苦想十分鐘卻只能頭疼時,您就知道這個 JAPH 是糟糕的了。
  
  清單 5. 代替和計算
  
  Date:     26 Mar 90 16:20:37 GMT
  From:     [email protected] (Raymond Chen)
  $_='x"Not ";"x\"another \";\'x\\"perl \\";x\\"hacker,\\"\'"';s/x/print/g;eval eval eval;
  
  這裡舉例說明的一個普通的技巧是用另外的單詞來代替原有的一個單詞,然後再計算輸出(實際上,在您進行這個步驟時正在建立 Perl 代碼)。上面的示例中,每一個"x"都被換成了"print"。您還應該懂得 Perl 中的引用規則。Perl 在那個字符串中計算之後看到的是:x"Not ";"x\"another \";'x\"perl \";x\"hacker,\"'"(在 s/// 命令前面加上一個 print() 來察看這一點。)
  
  字符串以一個單引號開頭,所以它也必須以一個單引號結束。如果您往前面找單引號,就會發現有兩個單引號轉義了(帶一個反斜槓),第三個才是真的。
  
  現在,運行替換(在 s/// 命令後面加上一個 print() 自己去看結果):print"Not ";"print\"another \";'print\"perl \";print\"hacker,\"'"
  
  接下來是我們的命令了。為什麼在這裡要運行三個 eval() 命令,而不是僅僅一個呢?仔細看一下。第二個 print() 是在字符串裡面的,並不會被第一個 eval() 計算。但第一個 eval() 會返回計算過的第一級字符串:print"another ";'print"perl ";print"hacker,"'。它將打印出“Not”。為什麼第一個 eval() 不返回字符串的第一部分?因為 eval() 只返回計算過的最後的東西。用“print eval”代替“eval eval eval”作為最後的語句,看看這樣操作的效果如何。
  
  第二個 eval 是做什麼的呢?它是用來計算第二個,而不是第三個或第四個 print() 語句的。如果您觀察一下就會發現它們兩個都是在一對單引號內的字符串裡的。第二個 eval 會返回含有第三個和第四個 print() 語句的字符串,留下剛剛打印了“another ”的那個語句。所以第二個 eval 將返回:print"perl ";print"hacker,"
  
  第三個 eval 會運行那兩個 print() 語句來結束這段 JAPH(奇怪的是,它會打印出“Not another perl hacker,”)。
  
  正如您所看到的,分解一段糟糕的 JAPH 是要花壞闶奔涞摹<詞故竅笪頤歉詹漚庖氲哪敲醇虻サ畝?鳎?詈蠖加瀉眉父龈叢擁牟愦巍?
  
  讓我們來分析另一個糟糕到極點的 JAPH :
  
  清單 6. Abigail 的天書
  
  #Abigail
  $_ = "\x3C\x3C\x45\x4F\x54"; s/<  Just another Perl Hacker
  EOT
  
  清單 6 看起來也象是一段簡單的 JAPH。為什麼?字符串就在那裡 — 有什麼神秘的地方?其實,Abigail 風格就是以一種新的方式來使用您以前所見過的東西。例如,在這裡,給操作符 s/// 加上了修飾符“e”。這樣就讓它在進行替換之前計算右邊的表達式。這樣一來,“<  
  編碼的 $_字符串最後包含“<  
  編碼過的字符串和看似簡單的替換是 JAPH 的支柱。尤其是替換,它可以用新的令人驚奇的方式進行,您會發現在您自己的代碼中這些方式很有用。
  
  下面是 Abigail 的另一段如惡魔般讓您絞盡腦汁的 JAPH:
  
  清單 7. Abigail 解釋的原型
  
  #Abigail
  perl -wle 'sub _ "Just another Perl Hacker"; print prototype \&_'
  
  理解這段 JAPH 需要一定的使用原型的知識。請參閱“perldoc -f sub”和“perldoc -f prototype”文檔來了解這是怎麼一回事。基本上,這建立了一個新的函數,它名為“_”,沒有函數體,但帶有一個“Just another Perl Hacker”的原型。
  
  如果您看了 Programming Perl,第三版(請參閱參考資料)關於原型的實際章節,您會發現原型不能是字符串。Abigail 很隨便地忽略了這個事實(因為這個函數永遠都不會用到),然後打印出了無效的原型。
  
  這樣合法嗎?可能吧,因為它並不引起程序崩潰。這樣瘋狂嗎?只有一點點。還有很多更瘋狂的方法,但這一種至少還證明了在定義函數時原型的合法性不會受到檢查。它還示范了我們可以定義一個名為“_”的函數 — 這種方法您不應該經常使用,因為它會和內建的“_”操作符沖突。
  
  難看的
  我們看過了良好的和糟糕的 JAPH,所剩下的就是難看的 JAPH 了。這些怪獸被精心打造,就是為了讓人們畏懼然後到處找藥吃,它們定義得太丑陋了。
  
  下面這一段 Kickstart 編寫的 JAPH 您應該送給對您非常重要的另一半(不包括家裡的寵物)。注意,稱其為一段難看的 JAPH 是不止一個原因的 — 四行的限制被遠遠的拋在一邊。
  
  清單 8. 燃燒的心
  
  #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 是: “AMOURtruecardsecstacycrushhonpromisedevotionpartnerstendertrueloverstreasureaffection
  devotioncarewoobabyardorromancingenthusiasmfealtyfondnessturtledovesloverssentiment
  worshipsweetlingpureattachmentflowersrosespromisepoem”,
  它告訴我們所有這些不加修飾的單詞都被 Perl 作為字符串來解釋。
  
  清單 9. 傾倒愛情
  
  #!/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。有時候我們還需要修改它們 — 只是為了讓事情變得有趣 — 但最終愛情會勝利。
  
  拆解後的腳本是:
  
  清單 10. 拆解後的愛情
  
  #!/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;

Copyright © Linux教程網 All Rights Reserved