http://qs321.pair.com?node_id=397597

…but why are there so many trees?

@j = ((qw)" Just " )), @a = ((qw " another" )), @p = ((qw " Perl " )), @h = ((qw " hacker " )), (()),),(s""@j,",s (" )))g), print

Makeshifts last the longest.

Replies are listed 'Best First'.
Re: I can see the forest…
by opqdonut (Acolyte) on Oct 09, 2004 at 15:35 UTC

    Nice one! Implicit evaluation of a used confusingly. Took me a little while to figure how you got the text inside $_ and the qw) trick is very ingenious too. A fine example of how beautiful perl can be :)

    Tips for the hopeless:

    • qw) starts a string that terminates at the next ) so actually the whole script is stuff assigned into @j!
    • the definition of @j stops at the second comma on line 5, so the rest is evaluated but not assigned anywhere!
    • the s""@j," puts @j into $_ and then the 's (" )))g' (which is actually s/" //g in disguise!) eliminates some (non-existant?) quotes
    • Finally, we get a 'print' that spews out $_ beautiful!

    J

      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:

      @j = "Just", @a = "another", @p = "Perl", @h = "hacker"; s//@j,/, prin +t

      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:

      @j = ( "Just", @a = "another", @p = "Perl", @h = "hacker" ); s//@j,/, +print

      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…

      @j = (( "Just" )), @a = (( "another" )), @p = (( "Perl", )), @h = (( "hacker" )),

      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 = (( qw) "Just"    )),
      @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

      @j = ( '"Just"', @a = 'another', @p = 'Perl', @h = 'hacker',

      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:

      @j = (( qw)"Just" )), @a = (( qw"another" )), @p = (( qw"Perl" )), @h = (( qw"hacker" )),

      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:

      @j = ((qw)" Just " )), @a = ((qw " another" )), @p = ((qw " Perl " )), @h = ((qw " hacker " )),

      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

      @j = ( '"', 'Just, '"', @a = 'another', @p = 'Perl', @h = 'hacker',

      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:

      $ perl -MO=Deparse japh.pl @j = (('"', 'Just', '"'), @a = 'another', @p = 'Perl', @h = 'hacker', +()), (s//@j,/, s/" //g), print($_); - syntax OK

      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:

      @j = ((qw)" Just " )), @a = ((qw " another" )), @p = ((qw " Perl " )), @h = ((qw " hacker " )), (()),),(s""@j,",s (" )))g), print

      Makeshifts last the longest.

Re: I can see the forest…
by Anonymous Monk on Oct 08, 2004 at 15:00 UTC
    Slightly off-topic... I display the Perl Monks RSS feed on my home-grown perl-generated bookmarks page. Today, this feed broke and wouldn't display. It turned out there's a funky character embedded in the title for this item, and my script didn't handle it very gracefully. Try this at home: wget http://www.perlmonks.org/headlines.rdf Look at the source, and you'll see "I can see the forest[]" where [] is the unprintable character. I don't know what it is in the RSS feed generator for this site that turned "..." into a new character, but I had to "decode" the string as if it were in UTF-8 in order for Perl to know what to do with it. So... public service announcement for you all. - Aaron
      FWIW, there's a special ellipses character in many character sets that combines three dots "..." into a special character number 224, “…". Some desktop applications may even convert these on the fly when the user types three periods in a row, in much the same way that Word converts normal quotes to curly quotes.