perldoc Acme::EyeDrops から引用。
こういう図形のことはしっていたけど、こういう名前だったのか。
シェルピンスキーのギャスケット - Wikipedia
$ perl -le '$x=2**pop;print$"x--$x,map$x&$_?$"x2:"/\\",0..$y++while$x' 4 /\ /\/\ /\ /\ /\/\/\/\ /\ /\ /\/\ /\/\ /\ /\ /\ /\ /\/\/\/\/\/\/\/\ /\ /\ /\/\ /\/\ /\ /\ /\ /\ /\/\/\/\ /\/\/\/\ /\ /\ /\ /\ /\/\ /\/\ /\/\ /\/\ /\ /\ /\ /\ /\ /\ /\ /\ /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
ぬは。すげーー!
解読してみる。
$ perl -MO=Deparse -le '$x=2**pop;print$"x--$x,map$x&$_?$"x2:"/\\",0..$y++while$x' 4 BEGIN { $/ = "\n"; $\ = "\n"; } $x = 2 ** pop(@ARGV); print $" x --$x, map(($x & $_ ? $" x 2 : '/\\'), 0 .. $y++) while $x;
最初に、$x に 2の<引数>乗の値が入る。引数が1なら2、引数が2なら4、引数が3なら8、...
$x = 2 ** pop(@ARGV);
で、この $x が0より大きい限りはwhile文として以下が繰り返される。
print $" x --$x, map(($x & $_ ? $" x 2 : '/\\'), 0 .. $y++)
ちなみに -l スイッチによって $\ = "\n" になっているので、print1回毎に改行される。
最初に空白を出力すると同時にこの $x をデクリメントしていく。
print $" x --$x
$" はデフォルトで半角スペース、それを x 演算子で $x 個つなげたもの、をまず出力している。これで三角形の左側の空白を作っているわけだ。
このデクリメントは別にprint関数内じゃなくてwhileの評価の部分でしてもいい気がする。
$ perl -le '$x=2**pop;print$"x$x,map$x&$_?$"x2:"/\\",0..$y++while$x--' 4 $ perl -MO=Deparse -le '$x=2**pop;print$"x$x,map$x&$_?$"x2:"/\\",0..$y++while$x--' 4 BEGIN { $/ = "\n"; $\ = "\n"; } $x = 2 ** pop(@ARGV); print $" x $x, map(($x & $_ ? $" x 2 : '/\\'), 0 .. $y++) while $x--;
というように書き換えても動作は同じだと思う。
ここからがメインの三角形の部分。
map(($x & $_ ? $" x 2 : '/\\'), 0 .. $y++)
$y はどこにも定義されていない変数なので、0から始まりwhile文が回る毎にインクリメントされていく。
ということはmap関数の第2引数に入るLISTはwhile文で繰り返されるたびに
(0) (0, 1) (0, 1, 2) (0, 1, 2, 3) ...
というように増えていくことになる。
で、このLISTに対しmap関数で行っている操作。
($x & $_ ? $" x 2 : '/\\')
ぱっと見ただけではよく分からない…
実際に $x をデクリメントさせ LIST を増加させながら見てみよう。
$ perl -le 'print map { 7 & $_ ? " " : "/\\" } 0 .. 0' /\ $ perl -le 'print map { 6 & $_ ? " " : "/\\" } 0 .. 1' /\/\ $ perl -le 'print map { 5 & $_ ? " " : "/\\" } 0 .. 2' /\ /\ $ perl -le 'print map { 4 & $_ ? " " : "/\\" } 0 .. 3' /\/\/\/\ $ perl -le 'print map { 3 & $_ ? " " : "/\\" } 0 .. 4' /\ /\ $ perl -le 'print map { 2 & $_ ? " " : "/\\" } 0 .. 5' /\/\ /\/\ $ perl -le 'print map { 1 & $_ ? " " : "/\\" } 0 .. 6' /\ /\ /\ /\ $ perl -le 'print map { 0 & $_ ? " " : "/\\" } 0 .. 7' /\/\/\/\/\/\/\/\/
なるほ…ど…?なんとなく見えてきたような…?
0 から $y までの数値のうち、$x との論理積が真(0以外の値)になるときだけ空白になっている。
つまり1つ以上の共通のビットが立っているときだけが真。
もうちょい並べて考えてみる。
$x 0..$y f(1111) 0 e(1110) 01 d(1101) 0*2 c(1100) 0123 b(1011) 0***4 a(1010) 01**45 9(1001) 0*2*4*6 8(1000) 01234567 7(0111) 0*******8 6(0110) 01******89 5(0101) 0*2*****8*a 4(0100) 0123****89ab 3(0011) 0***4***8***c 2(0010) 01**45**89**cd 1(0001) 0*2*4*6*8*a*c*e 0(0000) 0123456789abcdef
'*'に置き換えたところが論理積が真になる部分。
確かにシェルピンスキーのギャスケットを描いている。
こう書いてみると、なんとなく納得はできる。
この性質を利用してやれば最初のワンライナーでたしかにあの図形が描けるね、と。
けど、どうしてこうやって作ることができるのかが分からない。
このアイデアが天才的すぎて、考えたヤツ変態だ、としか思えない。。。
デクリメントする数値と、増加していくリストの各要素との「論理積」をとることでこのシェルピンスキーのギャスケットを描けるようになる原理を知りたい。
もうちょっと考えてみよう。
もしくは誰か頭の良いヒト教えてくれないかなー