> That's one convoluted JAPH!

Thanks.

> I like to pick through admirable JAPH's like this in an 
> attempt to learn more Perl idiosyncrasies [...]
> Anyone care to explain what's going on?

How about I tell you how I created it, which I think makes it easier to see
what's going on.

Every JAPH starts out with a small idea on some transormation of the phrase
"Just Another Perl Hacker". I noticed there are repeated letters in that
phrase, so I decided to collapse the phrase down to just the unique letters
and then rebuild the phrase by indexing them.

  # Reduce to unique characters
  $japh = 'Just another Perl hacker';
  $letter{$_}++ foreach split//, $japh;
  print join "", sort keys %letter;

So now I'm working with the string " JPacehklnorstu". But that string
doesn't have to be in any particular order, so it's off to the Internet
Anagram Server (http://www.wordsmith.org/anagram/) to make things
interesting. There are lots of funny results, and I ended up making a few
different versions of the JAPH with them. The one you saw was "steal Porch
Junk".

So now, I just create a list of indices to extract the proper phrase back
out. I could simply use the following:

  @indices = (12,13,0,1,5,3,14,7,1,10,2,8,5,6,2,8,4,5,10,3,9,15,2,8);

But that would be boring. And take up quite a bit of space. I noticed that
the range is 0..15 or one nybble (half a byte). I could pack all 24 indices
in just 12 bytes! I decided to represent the bytes as hex characters:

  # pack 'em into bytes.
  while (@indices) {
        $byte = (shift(@indices)<<4)|shift(@indices);
        printf('%x',$byte);
  }

So now I'm working with the string "cd0153e71a28562845a39f28". To extract
the indices, I just have to use hex() on each character in that string.

So now, let's put together a first cut at the JAPH, using the techniques
above:

  $_="steal Porch Junk"; @_=split //;  # put the letters in @_
  $_="cd0153e71a28562845a39f28";       # put the indices in $_
  s/(.)/$_[hex($1)]/ge;                # replace each index with its letter
  print;                               # print it out

Now the fun part... let's obfuscate it a bit. Adding another "e" to the
regex is low-hanging fruit. We just assemble the '$_[hex($1)]' part via
concatenated strings:

  s/(.)/'$_[' . 'hex' . '($1)]'/gee;

I don't like the quotes. Too obvious. Lets change them to q() but use '.'
instead of the parens (anyone who gets confused by this needs to read
perldoc perlop):

  s/(.)/q.$_[. . q.hex. . q.($1)]./gee;

Looking better, but that 'hex' stands out like a sore thumb. Noticing that
the "h" and "e" are in our letter list (@_), I decide to assemble the 'hex',
which is just a string at this point, from those letters ("h" = $_[10], "e"
= $_[2]), leaving the 'x':

  s/(.)/q.$_[. . $_[10] . $_[2] . q.x. . q.($1)]./gee

Better. Now let's get rid of the quotes from the rest of the JAPH too, using
q.. and q==

  $_=q;steal Porch Junk;;split //;
  $_=q=cd0153e71a28562845a39f28=;
  s/(.)/q.$_[. . $_[10] . $_[2] . q.x. . q.($1)]./gee;
  print;

Now let's break up that hex string in the second line. I see it has three
"28"'s, so we can do a substitution. What should we replace it with? The
string "s/" is about as diabolical as any. ";q" would have been a good one
too. I chose the former. In our y// (or y;; to be tricky) lets also replace
the "a"'s with ";". We'll also insert a newline, which we take back out with
a s/\n//;

  $_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
  s/56s/45;39fs/=;
  y;\;s/;a28;;
  s;\n;;;
  s/(.)/q.$_[. . $_[10] . $_[2] . q.x. . q.($1)]./gee;
  print;

Now let's smash it up a bit:

  $_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
  s/56s/45;39fs/=;y;\;s/;a28;;s;\n;;;
  s/(.)/q.$_[..$_[10].$_[2].q.x..q.($1)]./gee;print;
  
Looking kinda JAPHy now, isn't it? There's a few more things we can do. The
y operator can take extra chars at the end without consequence. We'll add
another decoy 's/'. In the first substitution, let's replace '\n' with '$/'.

  $_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
  s/56s/45;39fs/=;y;\;s/;a28s/;;s;$/;;;
  s((.))/q.$_[..$_[10].$_[2].q.x..q.($1)]./gee;print;

Now, let's work on the substitution again. We can split it between lines,
and change s/// to s()//. Also we can change the ';' before print to a '/',
which is interpreted as a divide, but will still evaluate the 'print' with a
warning under -w which we don't care about.

  $_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
  s/56s/45;39fs/=;y;\;s/;a28s/;;s;$/;;;s((.))/
  q.$_[..$_[10].$_[2].q.x..q.($1)]./gee/print;

More stuff: Let's insert some ';' where they won't make a difference, and
remove one from the end. Then I want to pad the last line by 2 chars because
I like to make the lines equal length. We can add $. (which contains nothing
outside a loop) to the substitution string.

  $_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
  s/56s/45;39fs/=;y;\;s/;a28s/;;;s;$/;;;s((.))/;
  q.$_[.$..$_[10].$_[2].q.x..q.($1)]./gee/print;

More can always be done, but at this point it looks good enough for me.

Hope you found this interesting.


-- 
Mark Thomas                 [EMAIL PROTECTED] 
Internet Systems Architect     DigitalNet, Inc. 

$_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
s/56s/45;39fs/=;y;\;s/;a28s/;;;s;$/;;;s((.))/;
q.$_[..$..$_[10].$_[2].q.x..q.($1)]./gee/print

_______________________________________________
Perl-Win32-Users mailing list
[EMAIL PROTECTED]
To unsubscribe: http://listserv.ActiveState.com/mailman/mysubs

Reply via email to