|No such thing as a small change|
Yep, though you missed a few bits. :-) I'll get to that eventually.
This started with a much simpler idea. What I really wanted to do was:
I was hoping that @j would actually be assigned all the values the assignments to its right evalute to as well, so it would become ("Just", "another", "Perl", "hacker"), except a human reader would be caught off guard by the commas, wondering just for a second where all the other words came from when none of @a @p @h are actually ever used.
Using s//$foo/ to assign $foo to $_ is actually standard fare in obfuscations. You'll see that a lot. I added it here to add a slight air of obfuscation that would keep people off balance for just a moment longer.
Unfortunately, Perl DWIMs too well here. :-( Larry did a very good job. :-) To keep with the idea I had to introduce a pair of parens:
Of course that's a dead give-away. So I had to find a way to hide the fact that there's a paren there; the easiest way to deceive humans is to lull them into complacence by feigning structure with repeating patterns, to make them misinterpret which parts belong together. Creative use of spacing and indentation is very helpful here — how often have you been mocked by a piece of misleadingly indented code whose problems became immediately obvious once you fixed the indentation? So I added a bunch of parens…
Now, I had to somehow eat one of the two closing parens on the first line. The easiest way is to use it as a delimiters for some quote-like operator; I chose qw// because it discards extraneous spacing, of which there'll be a bunch. Pay attention to where the sequence stretches to now:
@j = (( ), @a = (( "another" )), @p = (( "Perl", )), @h = (( "hacker" )),
All that whitespace is part of the qw//, but it does not end up in the list generated by it. However, the quotes are part of the first element in that list — they'll need to be removed later. In effect, this is equivalent to
Behold — one paren on the first line is without a partner now! :-)
But there's this pesky qw// in the first line now that indicates an obvious difference. Lets put some of those in the other lines to give the appearance of regularity and structure again:
Here, the quotes no longer indicate double quoted strings — they're actually delimiters for the qw// sequences. Since that one discards whitespace to return a list of whitespace separate words, we can add a little more space. And Perl will happily ignore whitespace between the qw and the first delimiter, so I lined up all the quotes to strengthen the appearance of structure:
I removed the initial space as well so that for the first qw// because human readers are conditioned to instinctively match up pairs of delimiters as they read — so they promptly see (qw) when they look at it.
Note that adding spaces on the first line where the quotes are part of qw// literal changes the result — the code is now equivalent to
If you print "@j" now (or, indeed, s//@j/, print) you will get
" Just " another Perl hacker
And that's the reason for that s/" //g you didn't figure out.
Btw, you can cut a long story short on this obfu by deparsing it:
It is all about misleading the reader. The same tools that can be used to add cues and structure to code to make it readable can also be abused to add cues and structure that disagree with reality — plain old lies. I found myself wishing Perl wasn't quite designed so well though — I had hoped not to have to use any deception. Oh well. :-) It was still a fun diversion and is actually a JAPH I like, all the same.
In hindsight, it would have been a little more effective yet to lay it out a little broader:
Makeshifts last the longest.