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のミームである「JustanotherPerlhacker」</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リスト演算子です。いずれにせよ、関数または非null演算子名の直後にプラス記号を付けることは+二項演算子として使用できないため、最初の両方の+記号は単項演算子です。この奇妙な点は、マニュアルに記載されています。

括弧を追加し、のブロック構文を使用し、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*', ARRAYです。のc*修飾子は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 に答える