这个Perl单行程如何实际工作?

时间:2012-06-01 22:48:43

标签: perl

所以,我碰巧注意到last.fm正在我的地区招聘,因为我认识了一些人who worked there,但我申请了。

但我想我最好首先看一下current staff

该页面上的每个人都有一个可爱/聪明/愚蠢的标语,比如“生命不是我们自己的一千倍太短了吗?”。事实上,在我达到这个目标之前,它非常有趣:

perl -e'print+pack+q,c*,,map$.+=$_,74,43,-2,1,-84, 65,13,1,5,-12,-3, 13,-82,44,21, 18,1,-70,56, 7,-77,72,-7,2, 8,-6,13,-70,-34'

我无法抗拒粘贴到我的终端(也许是一种愚蠢的事情),但它打印出来了:

  

另一个Last.fm黑客,

我认为弄清楚Perl单线程如何工作会相对容易。但我真的无法理解文档,我不知道Perl,所以我甚至不确定我是否正在阅读相关文档。

所以我尝试修改数字,这让我无处可去。所以我认为它真的很有趣,值得一试。

所以,“它是如何工作的”有点模糊,我的问题主要是,

这些数字是多少?为什么有负数和正数,否定性或积极性是否重要?

运营商+=$_的组合是什么?

pack+q,c*,,在做什么?

2 个答案:

答案 0 :(得分:29)

这是“Just another Perl hacker”的一个变种,一个Perl meme。随着JAPH的到来,这个相对温和。

您需要做的第一件事是弄清楚如何解析perl程序。它缺少函数调用的括号,并以有趣的方式使用+和类似引号的运算符。原始程序是这样的:

print+pack+q,c*,,map$.+=$_,74,43,-2,1,-84, 65,13,1,5,-12,-3, 13,-82,44,21, 18,1,-70,56, 7,-77,72,-7,2, 8,-6,13,-70,-34

pack是一个函数,而printmaplist operators。无论哪种方式,紧跟一个加号的函数或非空运算符名称都不能使用+作为二元运算符,因此开头的+个符号都是unary operators。这种奇怪的行为在manual

中有所描述

如果我们添加括号,请使用map的块语法,并添加一些空格,我们得到:

print(+pack(+q,c*,,
            map{$.+=$_} (74,43,-2,1,-84, 65,13,1,5,-12,-3, 13,-82,44,21,
                         18,1,-70,56, 7,-77,72,-7,2, 8,-6,13,-70,-34)))

下一个棘手的问题是q这里是q quote-like operator。它通常用单引号编写:

print(+pack(+'c*',
            map{$.+=$_} (74,43,-2,1,-84, 65,13,1,5,-12,-3, 13,-82,44,21,
                         18,1,-70,56, 7,-77,72,-7,2, 8,-6,13,-70,-34)))

请记住,一元加号是一个无操作(除了强制标量上下文),因此现在应该看起来更熟悉了。这是对pack函数的调用,格式为c*,表示“任意数量的字符,由当前字符集中的数字指定”。另一种写这个的方法是

print(join("", map {chr($.+=$_)} (74, …, -34)))

map函数按顺序将提供的块应用于参数列表的元素。对于每个元素,$_设置为元素值,map调用的结果是通过对连续元素执行块返回的值列表。编写此程序的更长方法是

@list_accumulator = ();
for $n in (74, …, -34) {
    $. += $n;
    push @list_accumulator, chr($.)
}
print(join("", @list_accumulator))

$.变量包含运行的总数。选择数字以使运行总数为作者想要打印的字符的ASCII代码:74 = J,74 + 43 = 117 = u,74 + 43-2 = 115 = s等等。取决于每个字符是按照ASCII顺序在前一个字符之前还是之后,它们都是负数或正数。

为了您的下一个任务,请解释此JAPH(由EyesDrop生成)。

''=~('(?{'.('-)@.)@_*([]@!@/)(@)@-@),@(@@+@)'
^'][)@]`}`]()`@.@]@%[`}%[@`@!#@%[').',"})')

请勿在生产代码中使用任何此类内容。

答案 1 :(得分:22)

这背后的基本思想非常简单。您有一个包含字符的ASCII值的数组。为了使事情变得更复杂,你不要使用绝对值,而是使用相对的值,除了第一个。因此,我们的想法是将特定值添加到前一个值,例如:

  1. 74 - > J
  2. 74 + 43 - > u
  3. 74 + 42 +( - 2) - > s
  4. 即使$.是Perl中的特殊变量,但在这种情况下并不意味着什么特别的。它仅用于保存以前的值并添加当前元素:

    map($.+=$_, ARRAY)
    

    基本上它意味着将当前列表元素($_)添加到变量$.。这将返回一个新数组,其中包含新句子的正确ASCII值。

    Perl中的q函数用于单引号的文字字符串。例如。你可以使用像

    这样的东西
    q/Literal $1 String/
    q!Another literal String!
    q,Third literal string,
    

    这意味着pack+q,c*,,基本上是pack 'c*', ARRAYc*中的pack修饰符将值解释为字符。例如,它将使用该值并将其解释为字符。

    它基本归结为:

    #!/usr/bin/perl
    use strict;
    use warnings;
    
    my $prev_value = 0;
    
    my @relative = (74,43,-2,1,-84, 65,13,1,5,-12,-3, 13,-82,44,21, 18,1,-70,56, 7,-77,72,-7,2, 8,-6,13,-70,-34);
    my @absolute = map($prev_value += $_, @relative);
    
    print pack("c*", @absolute);