21

所以,我碰巧注意到 last.fm 正在我所在的地区招聘,因为我认识一些 那里工作的人,所以我想申请。

但我想我最好先看看现在的员工

该页面上的每个人都有一条可爱/聪明/愚蠢的标语,例如“生命不是太短一千倍,我们不会让自己无聊吗?”。事实上,这很有趣,直到我明白了这一点:

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*,,

4

2 回答 2

29

这是Perl meme “Just another Perl hacker”</a> 的变体。随着 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是一个函数,而printmap列表运算符。无论哪种方式,紧跟加号的函数或非空运算符名称都不能+用作二元运算符,因此+开头的两个符号都是一元运算符手册中描述了这种奇怪之处。

如果我们添加括号,使用块语法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 类似于引号的操作符。它更常用单引号写成:

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制作)。

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

不要在生产代码中使用任何这些。

于 2012-06-01T23:41:42.660 回答
22

这背后的基本思想非常简单。您有一个包含字符的 ASCII 值的数组。为了使事情变得更复杂一点,您不使用绝对值,而是使用除第一个以外的相对值。所以想法是在前一个的基础上加上具体的值,例如:

  1. 74 ->J
  2. 74 + 43 ->u
  3. 74 + 42 + (-2) ->s

尽管$.它是 Perl 中的一个特殊变量,但在这种情况下它并不意味着任何特殊。它只是用于保存之前的值并添加当前元素:

map($.+=$_, ARRAY)

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

Perl 中的q函数用于单引号的文字字符串。例如,您可以使用类似的东西

q/Literal $1 String/
q!Another literal String!
q,Third literal string,

这意味着pack+q,c*,,基本上是pack 'c*', ARRAYc*修饰符 in将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);
于 2012-06-01T23:16:38.737 回答