27、探索 Perl 编程的趣味世界

探索 Perl 编程的趣味世界

1. Just Another Perl Hacker(JAPH)

JAPH 起源于 Perl 黑客 Randal Schwartz 在 Usenet 帖子结尾的签名 “Just another Perl hacker”,后来演变成用各种有趣、有教育意义或意想不到的方式输出这句话的 Perl 程序传统。

  • 示例代码解析
    • 若要解析复杂的 JAPH 代码,可使用 B::Deparse 模块。例如,对于代码:
($p{$_})&6];$p{$_}=/ ^$P/ix?$P:close$_}keys%p}p;p;p;p;p;map{$p{$_}=~/^[P.]/&&
close$_}%p;wait until$?;map{/^r/&&<$_>}%p;$_=$d[$q];sleep rand(2)if/\S/;print
可以使用以下命令:
% perl -MO=Deparse mjd-japh
它会输出解析后的 Perl 代码:
@P = split(??, '.URRUUxR', 0);
@d = split(??, "\nrekcah xinU / lreP rehtona tsuJ", 0);
sub p {
    @p{"r$p", "u$p"} = ('P', 'P');
    pipe *{"r$p"}, *{"u$p"};
    ++$p;
    ($q *= 2) += $f = !fork;
    map {$P = $P[$f | ord $p{$_} & 6];
    $p{$_} = / ^$P/xi ? $P : close *$_;} keys %p;
}
p ;
p ;
p ;
p ;
p ;
map {close *$_ if $p{$_} =~ /^[P.]/;} %p;
wait until $?;
map {<$_> if /^r/;} %p;
$_ = $d[$q];
sleep rand 2 if /\S/;
print $_;
`B::Deparse` 模块与 Perl 编译器 `O.pm` 配合使用,使用 `perl -MO=Deparse` 可输出经 Perl 解析器解析后再反解析的 Perl 代码。若需要更多提示,可使用 `-p` 选项添加额外的括号:
% perl -MO=Deparse,-p mjd-japh
部分输出如下:
(($q *= 2) += ($f = (!fork)));
map({($P = $P[($f | (ord($p{$_}) & 6))]);
  • 不同复杂度的 JAPH
    • 基本编码解码型 :对 JAPH 字符串进行编码后再解码,代码如下:
$_ = q ;4a75737420616e6f74686572205065726c204861636b65720as;;
for (s;s;s;s;s;s;s;s;s;s;s;s)
{
    s;(..)s?;qq qprint chr 0x$1 and \161 ssq;excess;
}
- **包含字符串但输出方式不明显型**:代码中明显包含 JAPH 字符串,但不清楚如何输出到标准输出,例如:
eval {die [[qq [Just another Perl Hacker]]]};; print
${${${@}}[$#{@{${@}}}]}[$#{${@{${@}}}[$#{@{${@}}}]}]
- **看似包含但其余部分不明型**:代码看起来可能包含 JAPH 字符串,但其余部分不清楚,例如:
BEGIN {$^H {q} = sub {$_ [1] =~ y/S-ZA-IK-O/q-tc-fe-m/d; $_ [1]}; $^H= 0x28100}
print "Just another PYTHON hacker\n";
此 JAPH 依赖于 `overload` 编译指令的一个鲜为人知的特性以及其实现中依赖于在魔术变量 `$^H` 和 `%^H` 中放置特定值的特性。
  • Abigail 的 JAPH 示例
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %;
BEGIN {% % = ($ _ = " " => print "Just another Perl Hacker\n")}
虽然最后一行明确输出了 JAPH 字符串,但整个代码能作为有效的 Perl 代码解析令人惊讶。这是因为 `//` 是有效的模式匹配,`%%` 是有效的哈希名称,`**` 是有效的通配符。
2. Perl 高尔夫

Perl 高尔夫是由 Uri Guttman 发明的游戏,玩家需用尽可能少的字符解决编程问题。

  • 斐波那契数列生成示例
    • 最初的代码:
perl -e '$a=$b=1; while (1) {$c= $a+$b; print $c,"\n"; $a=$b; $b=$c; }'
此代码字符数较多,可通过去除多余空格和使用 `-l` 选项改进:
perl -le '$a=$b=1;while(1){$c=$a+$b;print$c;$a=$b;$b=$c;}'
字符数从较多减少到 48(`-l` 算一个字符),但仍超过预期。进一步优化,利用赋值和打印操作总是返回真值的特点:
perl -le '$a=$b=1;while(print$c=$a+$b){$a=$b;$b=$c;}'
字符数降至 43。还可去除初始的 `$a=` 和最后的分号:
perl -le '$b=1;while(print$a+$b){($a,$b)=($b,$a+$b)}'
但字符数仍为 43。换一种思路,在循环中同时生成两个项:
perl -le '$b=1;while(1){$a+=$b;print$a;$b+=$a;print$b}'
字符数增加到 45,继续优化得到对称代码:
perl -le '$b=1;print$a+=$b while print$b+=$a'
字符数为 35。再进一步:
perl -le 'print$a+=$b while print$b+=$a||1'
字符数为 33。最后使用特殊变量代替 `$b` 可再减少一个字符:
perl -le 'print$a+=$}while print$}+=$a||1'
最终字符数为 32。
  • $A++ 挑战
    挑战是将 $A 的值加 1,目前有 288 个参赛作品,从简单的 $A++; 到复杂的:
y ccccd x s vvchr oct oct ord uc ave x s vvucve le
s vvuc ave x s vvchr oct oct oct ord uc bve x eval
复杂代码字符数达 61。
3. Perl 诗歌

Perl 诗歌是编写具有文学价值而非实用价值的有效 Perl 程序。

  • 发展历程
    • 起源于 Larry 的第一个 Perl 俳句:
print STDOUT q
Just another Perl hacker,
unless $spring
但 Perl 俳句存在问题,如需要读者对音节约定达成一致。后来 Perl 诗歌趋向于抽象,通常没有韵律或音节结构。最著名的 Perl 诗歌示例是 Larry 的 Black Perl 诗歌。
- 目前 Perl 诗歌的实践呈下降趋势,部分原因是缺乏兴趣,部分原因是 Larry 努力确保 Black Perl 不能在现代版本的 Perl 上运行。不过仍有偶尔的 Perl 诗歌竞赛和在 `perlmonks.org` 诗歌页面上的投稿。
  • 示例代码
for(long => time) {$early && $self->went($bed);}
rand time && do {
    while ($candle--) {
        (time => $eyes->shut( )) < (time => print "Falling asleep!")
    }
};
4. 单行代码

单行代码文化与 Perl 高尔夫相关,旨在用一行 Perl 代码完成尽可能多的任务,有时采用意想不到的方式。

  • 文件行数统计示例
    • 使用 -n 开关和计数器:
perl -nle '$count++; END{print $count}'
- 使用 Perl 内置的行计数器 `$.`:
perl -nle 'END{print $.}'
- 专家级单行代码示例:
perl -wlpe '}{$_=$.'
  • 素数测试示例
perl -le '(1x shift)=~/^1?$|^(11+?)\1+$/||print"Prime"'
5. Acme 模块

CPAN 上的 Acme 命名空间用于存放不太严肃的 Perl 模块,很多模块源于 London.pm。

  • Acme::Bleach
    • 示例程序:
use Bleach;
for my $i (1..10) {
    print "Hello! $i\n";
}
第一次运行时似乎什么都不做,但再次查看代码时,代码变为:
use Bleach;
且仍能正常运行并打印 10 次消息。其核心代码如下:
my $tie = " \t"x8;
sub whiten { local $_ = unpack "b*", pop; tr/01/\t/; s/(.{9})/$1\n/g; $tie.$_ }
sub brighten { local $_ = pop; s/^$tie|[^ \t]//g; tr/ \t/01/; pack "b*", $_ }
sub dirty { $_[0] =~ /\S/ }
sub dress { $_[0] =~ /^$tie/ }
open 0 or print "Can't rebleach'$0'\n" and exit;
(my $shirt = join "", <0>) =~ s/.*^\s*use\s+Acme::Bleach\s*;\n//sm;
local $SIG{__WARN__} = \&dirty;
do {eval brighten $shirt; exit} unless dirty $shirt && not dress $shirt;
open 0, ">$0" or print "Cannot bleach '$0'\n" and exit;
print {0} "use Acme::Bleach;\n", whiten $shirt and exit;
该模块的工作原理是将代码转换为二进制表示,再将 0 和 1 转换为不同类型的空白字符,根据代码是否已处理决定是恢复代码还是再次处理。
  • Acme::Eyedrops
    该模块可自动混淆普通的 Perl 程序,例如对于 print "hello world\n"; ,使用以下命令:
perl -MAcme::EyeDrops=sightly -e 'print sightly("helloworld.pl")'
输出如下:
eval eval
'"'.('['^'+').('['^')').('`'|')').('`'|'.').('['^'/').('{'^'[').'\\'.'"'.
('`'|'(').('`'|'%').('`'|',').('`'|',').('`'|'/').('{'^'[').('['^',').('`'
|'/').('['^')').('`'|',').('`'|'$').'\\'.'\\'.('`'|'.').'\\'.'"'.';'.('!'^
'+').'"'
它将程序中每个字符的 ASCII 值进行编码,还能将编码后的符号排列成漂亮的形状。
  • Acme::Your
    该模块类似于 our ,但可使用来自不同包的未限定包变量,是对源过滤器的创造性(可能甚至有用)应用。

  • Acme::Chef
    这是 David Morgan - Mar 的 Chef 编程语言的实现,程序以食谱的形式表达,例如:

Hello World Souffle.
This recipe prints the immortal words "Hello world!", in a basically
brute force way. It also makes a lot of food for one person.
Ingredients.
72 g haricot beans
101 eggs
108 g lard
111 cups oil
32 zucchinis
119 ml water
114 g red salmon
100 g dijon mustard
33 potatoes
Method.
6. 源过滤器

源过滤器由 Paul Marquess 发明,用于在 Perl 源代码到达解析器之前进行拦截。使用 Filter::Util::Call 中的函数可将 Perl 子例程安装到输入流中。

  • 使用 Filter::Simple 示例
package Filter::Rot13;
use Filter::Simple;
FILTER { tr[a-z][n-za-m]; }
当代码使用 `Filter::Rot13` 时,`use` 语句之后的所有代码将通过 `FILTER` 块,将字母表旋转 13 位。许多 Acme 模块的实用性源于 `Filter::Simple`,而 `Switch` 模块可能是对源过滤器最全面的应用,它为 Perl 提供了 `switch - case` 语句的实现。

总结

Perl 编程的趣味世界充满了各种独特的玩法和技巧,从 JAPH 到 Perl 高尔夫、Perl 诗歌、单行代码,再到 Acme 模块和源过滤器,每个领域都有其独特的魅力和挑战。无论是追求代码的简洁性、创造性还是文学性,Perl 都能满足你的需求。希望通过本文的介绍,你能对 Perl 编程的这些有趣方面有更深入的了解,并在实际编程中尝试运用这些技巧。

流程图:Perl 高尔夫优化流程

graph TD;
    A[初始代码: perl -e '$a=$b=1; while (1) {$c= $a+$b; print $c,"\n"; $a=$b; $b=$c; }'] --> B[去除空格和使用 -l 选项: perl -le '$a=$b=1;while(1){$c=$a+$b;print$c;$a=$b;$b=$c;}'];
    B --> C[利用赋值和打印特性: perl -le '$a=$b=1;while(print$c=$a+$b){$a=$b;$b=$c;}'];
    C --> D[去除初始 $a= 和最后分号: perl -le '$b=1;while(print$a+$b){($a,$b)=($b,$a+$b)}'];
    D --> E[循环中生成两个项: perl -le '$b=1;while(1){$a+=$b;print$a;$b+=$a;print$b}'];
    E --> F[对称代码: perl -le '$b=1;print$a+=$b while print$b+=$a'];
    F --> G[添加逻辑或操作: perl -le 'print$a+=$b while print$b+=$a||1'];
    G --> H[使用特殊变量: perl -le 'print$a+=$}while print$}+=$a||1'];

表格:不同 JAPH 类型示例

JAPH 类型 示例代码 特点
基本编码解码型 $_ = q ;4a75737420616e6f74686572205065726c204861636b65720as;; for (s;s;s;s;s;s;s;s;s;s;s;s) {s;(..)s?;qq qprint chr 0x$1 and \161 ssq;excess;} 对 JAPH 字符串进行编码后再解码
包含字符串但输出方式不明显型 eval {die [[qq [Just another Perl Hacker]]]};; print ${${${@}}[$#{@{${@}}}]}[$#{${@{${@}}}[$#{@{${@}}}]}] 代码中明显包含 JAPH 字符串,但不清楚如何输出到标准输出
看似包含但其余部分不明型 BEGIN {$^H {q} = sub {$_ [1] =~ y/S-ZA-IK-O/q-tc-fe-m/d; $_ [1]}; $^H= 0x28100} print "Just another PYTHON hacker\n"; 代码看起来可能包含 JAPH 字符串,但其余部分不清楚

探索 Perl 编程的趣味世界

6. 源过滤器(续)

源过滤器的核心在于对 Perl 源代码进行预处理,为编程带来更多的灵活性和创意。除了前面提到的 Filter::Simple 模块应用,我们还可以深入了解其工作原理和更多应用场景。

  • 工作原理 :源过滤器通过拦截输入流中的源代码,在代码到达 Perl 解析器之前进行修改。例如, Filter::Rot13 模块将字母表旋转 13 位,这实际上是在解析之前对代码进行了转换。其内部机制是利用 Filter::Util::Call 中的函数,将自定义的子例程插入到输入流的处理流程中。
  • 更多应用场景 :源过滤器不仅可以用于字符转换,还可以用于代码的优化、调试信息的插入等。例如,我们可以编写一个源过滤器,在代码中自动插入日志语句,方便调试。以下是一个简单的示例:
package Filter::Logging;
use Filter::Simple;
FILTER {
    s/^(\s*)(.*)$/$1print "Executing line: $2\n"; $&/mg;
}

当代码使用 Filter::Logging 时,每行代码执行前都会打印出相应的日志信息。

7. 不同模块的综合应用

在实际编程中,我们可以将不同的 Acme 模块和源过滤器结合使用,创造出更有趣、更实用的程序。

  • Acme::Bleach 与源过滤器结合 :我们可以在使用 Acme::Bleach 处理代码的基础上,再应用一个源过滤器对代码进行进一步的修改。例如,在代码被漂白后,使用 Filter::Rot13 对代码进行加密:
use Acme::Bleach;
use Filter::Rot13;

for my $i (1..10) {
    print "Hello! $i\n";
}

这样,代码不仅会被漂白,还会被加密,增加了代码的安全性和趣味性。
- Acme::Eyedrops 与 Perl 高尔夫结合 :在进行 Perl 高尔夫时,我们可以使用 Acme::Eyedrops 对代码进行混淆,然后再尝试用最少的字符解决问题。例如,对于斐波那契数列生成的代码,先使用 Acme::Eyedrops 进行混淆:

perl -MAcme::EyeDrops=sightly -e 'print sightly("fibonacci.pl")'

然后对混淆后的代码进行优化,看看能否在更少的字符内实现相同的功能。

8. 总结与展望

通过对 Perl 编程中 JAPH、Perl 高尔夫、Perl 诗歌、单行代码、Acme 模块和源过滤器的探索,我们发现 Perl 是一门充满创意和乐趣的编程语言。

  • 总结 :JAPH 体现了 Perl 社区的创造力和幽默感,通过各种方式输出特定字符串,展示了 Perl 代码的多样性。Perl 高尔夫挑战了程序员用最少的字符解决问题的能力,锻炼了代码的简洁性。Perl 诗歌则将编程与文学相结合,为编程赋予了新的艺术价值。单行代码文化让我们看到了 Perl 在简洁表达方面的强大能力。Acme 模块为 Perl 编程带来了更多的趣味性和实用性,而源过滤器则为代码的预处理提供了强大的工具。
  • 展望 :未来,我们可以期待更多有趣的 Acme 模块的出现,以及源过滤器在更多领域的应用。同时,随着 Perl 社区的不断发展,JAPH、Perl 高尔夫等活动也将不断创新,为 Perl 编程带来更多的惊喜。我们也可以尝试将这些技巧应用到实际项目中,提高代码的质量和开发效率。

表格:不同模块的特点和应用场景

模块名称 特点 应用场景
Acme::Bleach 代码漂白,改变代码外观但不影响功能 代码保护、代码隐藏
Acme::Eyedrops 代码混淆,将代码转换为复杂的形式 代码加密、代码趣味性展示
Acme::Your 可使用不同包的未限定包变量 多包编程、代码复用
Acme::Chef 以食谱形式表达程序 创意编程、教学演示
Filter::Rot13 字母表旋转 13 位 代码加密、字符转换
Filter::Logging 自动插入日志语句 代码调试、信息记录

流程图:综合应用流程

graph TD;
    A[原始代码] --> B[使用 Acme::Bleach 处理]
    B --> C[使用 Filter::Rot13 加密]
    C --> D[使用 Acme::Eyedrops 混淆]
    D --> E[进行 Perl 高尔夫优化]
    E --> F[最终代码]

在 Perl 编程的趣味世界中,我们可以不断探索、创新,将各种技巧和模块结合起来,创造出更加独特、有趣的程序。希望大家在学习和使用 Perl 的过程中,能够充分体验到编程的乐趣。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值