1

Can anyone explain how does this code print that output?

Code

#!/usr/bin/perl
use warnings;
use strict;
       my          
      ($j,$        
   a,$p,$h);$      
   j=sub{print(    
  chr($p+=$a->[$   
  h++]));$j};;;$a  
 =[0,        split 
 "[:         \n]+", 
q/43         -002:1
-084         065:13
0001         000005
-0012        -00003
000013   -82 00048 
21:13:-6.00:-76:72 
 -007.:02:00008.00 
  :::-6.00:::013  
  -70:3::-70:.64   
    /];$p=0x4a     
      ;;$h=0;      
$j->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->();

Output:

Just another perl hacker

dlock
  • 9,447
  • 9
  • 47
  • 67

3 Answers3

6

This is actually a rather straightforward JAPH. The two biggest features of it is the splitting of the data string that makes up most of the "image", and the semi-recursive printing.

This is the code I get when I clean up the formatting

my ($j,$a,$p,$h);
$j = sub {
    print( chr( $p += $a->[$h++] ) );
    $j
};
;;

$a = [0, split "[: \n]+",   # the split regex
q/43         -002:1         # input string start: q/ ...
-084         065:13
0001         000005
-0012        -00003
000013   -82 00048 
21:13:-6.00:-76:72 
 -007.:02:00008.00 
  :::-6.00:::013  
  -70:3::-70:.64   
    /];                     # input string end:   .../   
# print Dumper $a;  # <--- this is my line
$p = 0x4a;;
$h=0;      

$j->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->();

The first part is the declaration and assignment of the 4 main variables:

$j

$j is a code reference that prints a character, then returns a reference to itself. The print consists of a few steps:

  1. $a->[$h++] iterates over the @$a array, returning one element
  2. $p += ... that element is added to $p
  3. print chr( $p ... ) then $p is returned to chr() and the resulting character is printed.

Last in the sub block is $j, which means each iteration, the return value of the sub will be a reference to itself. This is the part that allows for the semi-recursive feature.

$a

$a is a reference to an array that consists of 0 and the result of a split on the input string. Here also a few things are happening in one line of code:

  • q/43 .../ this is a regular q() single quoted string with the delimiter slash /. It ends right before the closing bracket ] that denotes the array reference.
  • split "[: \n]+", this is a split on a character class, repeated 1 or more times. There are extra spaces inside it (which I have removed here) to fill out the space in the JAPH itself. Basically this removes all colon, space and newline and returns the resulting list.
  • $a = [0, ... ] the list of the split is added to a list of 0 (just one element) and everything is surrounded with brackets to denote an array reference, which is then stored in $a.

I assume the list starts with 0 because the code has a one-off error, in that $p's original value is the first letter in the output.

This is the Data::Dumper output of $a:

$VAR1 = [
          0,
          '43',
          '-002',
          '1',
          '-084',
          '065',
          '13',
          '0001',
          '000005',
          '-0012',
          '-00003',
          '000013',
          '-82',
          '00048',
          '21',
          '13',
          '-6.00',
          '-76',
          '72',
          '-007.',
          '02',
          '00008.00',
          '-6.00',
          '013',
          '-70',
          '3',
          '-70',
          '.64'
        ];

$p and $h

$p and $h are only numbers, and their role in this is source number for the chr() function and iterator for the @$a array, respectively.

The last line is the part where the $j subroutine is run and the JAPH is printed. The chained ->() syntax means that the return value of each previous iteration is used for the next execution. And $j returns a reference to itself, which makes it a semi-recursive affair, only the values change.

I guess it is similar to doing this:

$x = $j->();
$y = $x->();
$z = $y->();
...

So lets walk through the first few steps

  • $j->() the execution
  • $a->[$h++] $h is incremented to 1, returns 0 to the $a substript, which returns the first element of $a, which is 0.
  • $p += 0 This 0 is added to $p, and this returns the value of $p (74) to chr
  • print chr(74) this now prints a J to standard output
  • $j is returned.
  • ->() another execution is performed on the return value, so $j is run again.
  • $a->[$h++] $h is incremented to 2, returns 1 to $a which returns the second element which is 43.
  • $p += 43 74 + 43 = 117 is returned to chr
  • print chr(117) this prints u

The elements of $a are both positive and negative, moving the number where it needs to be. In the end, all the letters printed form: Just another Perl hacker,

Here's a rundown on the 10 first executions:

p => 74  += 0         =  74 output => chr( 74) J
p => 74  += 43        = 117 output => chr(117) u
p => 117 += -002      = 115 output => chr(115) s
p => 115 += 1         = 116 output => chr(116) t
p => 116 += -084      =  32 output => chr( 32)
p => 32  += 065       =  97 output => chr( 97) a
p => 97  += 13        = 110 output => chr(110) n
p => 110 += 0001      = 111 output => chr(111) o
p => 111 += 000005    = 116 output => chr(116) t
p => 116 += -0012     = 104 output => chr(104) h
TLP
  • 66,756
  • 10
  • 92
  • 149
  • @deadlock It was a somewhat interesting algorithm to decode. :) – TLP Mar 06 '13 at 15:52
  • @deadlock Don't get me wrong, it was interesting, but compare it to this one: http://stackoverflow.com/q/9591658/725418 It really hurts your eyes when you look at it. :) – TLP Mar 06 '13 at 16:00
5

Run it with perl -MO=Deparse to see what Perl sees:

use warnings;
use strict 'refs';
my($j, $a, $p, $h);
$j = sub {
    print chr($p += $$a[$h++]);
    $j;
}
;
$a = [0, split(/[:         \n]+/, "43         -002:1\n-084         065:13\n0001         000005\n-0012        -00003\n000013   -82 00048 \n21:13:-6.00:-76:72 \n -007.:02:00008.00 \n  :::-6.00:::013  \n  -70:3::-70:.64   \n    ", 0)];
$p = 74;
$h = 0;
&$j()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->()->();

Basically, $j is a sub reference that returns another sub reference and so on. Before returning, it prints a character and moves the "pointer" into the array of characters to print.

choroba
  • 231,213
  • 25
  • 204
  • 289
1

Small comment.

The last line in the numbers block should be this

-68:3::-70:.64

instead of

-70:3::-70:.64   

It needs a -68 to print a period, the -70 was printing a comma.

(The rest of the line, ":3::-70:.64" is just noise. Those numbers are never passed to the anonymous function in $j.)

Justin
  • 48
  • 9