首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

功能丰富的 Perl JAPH 的精致 Just Another Perl Hacker-1

功能丰富的 Perl JAPH 的精致 Just Another Perl Hacker-1

本质上,JAPH 用四行或不到四行的代码(每行 80个或者更少的字符)来输出字符串“Just another Perlhacker”,这样一来 JAPH 就可以放在一个 USENET 签名中。USENET签名远远早于 Perl,这就使 JAPH 成了一个由长期存在的传统和 Perl的魔力结合在一起的奇异的混合体。
JAPH:“Justanother Perl hacker”据我们所知,JAPH 格式是在二十世纪九十年代由 Randal Schwartz推广的(好几处信息来源都同意这个说法)。今天,JAPH到处可见,它们是由该流派的那些不知疲倦的艺术家们制作的,比如comp.lang.lang.perl.misc 新闻组的 Abigail。
下面的讨论中我们将分析 CPAN (请参阅 )上的规范列表中的一些JAPH,它们适合初级到中级 Perl程序员。我在这里会对各种技巧作简单的说明,但是有兴趣的读者还是应该参阅                Programming Perl,第三版(请参阅 )来进一步学习。            
为了精确地支持这里所给的示例,您的系统中必须安装有 Perl5.6.0。最好您还安装了最新的(2000 或者更新版本)主流 UNIX系统(Linux、Solaris、BSD)。尽管这些示例也许能在老一点的 Perl 和UNIX版本甚至是其它操作系统上运行,您还是应该考虑一下如果它们运行失败这样需要解决的问题。每个JAPH 都以规范列表的格式显示,带有一个日期和作者属性。
优良的在开始讨论更丰富的内容之前,我们先来看看 Randal Schwartz 在 JAPH的早些时期编写的四段简单有趣的代码。我们下面的第一个示例证明了并不是所有的JAPH 都是要晦涩难懂的,有一些甚至是很容易看懂的。
清单 1. 一个简单的true 条件句
1
2
3
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..."




在清单 1 中,既然字符串不为空,                 if() 语句的值就始终为 true(只有空字符串“”、字符串“0”,数字 0 或其等价的表达形式,或者不定义时其值才为 false)。因而,将一直执行打印语句。            
清单 2. 使用Printf
1
2
3
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




清单 2 是另一个早期的样本,它使用                 printf() 的函数来产生所需的输出,这也证明了如果您愿意的话,Perl 看起来也可以象 C 语言一样。            
在清单 3 中 Schwartz 开始玩花招了。现在我们给                 print() 一个重新排列过的数组,然后将这个数组打印出来,单词之间有空格($ 是一个变量,它告诉 Perl 在一次打印所有数组元素时在元素之间应该放什么东西)。            
清单 3.重新排列数组
1
2
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] 是用来做什么的了。            
清单 4 是最早的有记录可查的 JAPH,还有点别出心裁,使用了                 split() 、                 sort() 和                 grep() :            
清单 4. Sort 然后grep
1
2
3
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” 。               
然后我们排序 ― 缺省情况下是按字母数字顺序 ― 得到:                 “1Just” “2another” “4Perl” “8hacker,” 。            
注意                 “10Just” 也应该排在                 "8hacker" 的前面 ― 这不是数字排序。            
排序后的列表被传递到                 grep() ,它将每个元素开头的所有数字都去掉,并在剩下的部分后面加上一个空格。结果是:                 “Just ” “another ” “Perl ” “hacker, ” 。            
最后,在这个列表上调用                 print() ,逐字逐元素打印。            
糟糕的看够了 JAPH的优点后,现在让我们来看看它真正“糟糕到极点”的地方。良好的 JAPH还有循序渐进的教学作用,糟糕的 JAPH却让您的思维混乱得象椒盐卷饼一样。当您盯着一段 JAPH冥思苦想十分钟却只能头疼时,您就知道这个 JAPH 是糟糕的了。
清单 5. 代替和计算
1
2
3
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;




这里举例说明的一个普通的技巧是用另外的单词来代替原有的一个单词,然后再计算输出(实际上,在您进行这个步骤时正在建立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() 只返回计算过的最后的东西。用“printeval”代替“eval evaleval”作为最后的语句,看看这样操作的效果如何。            
第二个 eval是做什么的呢?它是用来计算第二个,而不是第三个或第四个                 print() 语句的。如果您观察一下就会发现它们两个都是在一对单引号内的字符串里的。第二个eval 会返回含有第三个和第四个                 print() 语句的字符串,留下刚刚打印了“another ”的那个语句。所以第二个 eval将返回:                 print"perl ";print"hacker,"
第三个 eval 会运行那两个                 print() 语句来结束这段 JAPH(奇怪的是,它会打印出“Not another perl hacker,”)。            
正如您所看到的,分解一段糟糕的 JAPH是要花一点时间的。即使是象我们刚才解译的那么简单的东西,最后都有好几个复杂的层次。
让我们来分析另一个糟糕到极点的 JAPH :
清单 6. Abigail的天书
1
2
3
4
#Abigail
$_ = "\x3C\x3C\x45\x4F\x54"; s/<<EOT/<<EOT/e; print;
Just another Perl Hacker
EOT




清单 6 看起来也象是一段简单的 JAPH。为什么?字符串就在那里 ―有什么神秘的地方?其实,Abigail风格就是以一种新的方式来使用您以前所见过的东西。例如,在这里,给操作符s///加上了修饰符“e”。这样就让它在进行替换之前计算右边的表达式。这样一来,“<<EOT”就被自己替换成下一行到EOT 行之间的所有字符 ― 在这个示例中为“Just another PerlHacker”。
编码的 $_字符串最后包含“<<EOT”,所以操作符 s///进行的替换最后会将                字符串“<<EOT”替换成计算“<<EOT”的结果,即字符串“Justanother Perl Hacker”。                 print() 语句负责打印该字符串。            
编码过的字符串和看似简单的替换是 JAPH的支柱。尤其是替换,它可以用新的令人惊奇的方式进行,您会发现在您自己的代码中这些方式很有用。
下面是 Abigail 的另一段如恶魔般让您绞尽脑汁的 JAPH:
返回列表