in reply to foreach(silly..japh)

Just a reminder:

Syntax of for:

for (init something;test;do something) {
    stuff
}
In the first for, you don't init anything, have a long test, and do nothing, so you could also write the first line as a while-loop:

while ($_?$,:(${_} = q...q,.,.qq,$=,^q.$..$= => $_=q,.,.chr ord)) {
Here you first check if $_ is defined. If it is, you return $, which defaults to undef, thus ending the loop. If $_ is not defined (the default), we enter this statement:
${_} = q...q,.,.qq,$=,^q.$..$= => $_=q,.,.chr ord
Now, lets de-obfuscate:

${_} is the same as $_, the curly braces beeing used like in @{$arrayref}

q.. . q,., . qq,S=, ^ q.$. . $= => $_=q,., . chr ord
looks like
 '' . '.'  .   "$=" ^  '$' . $= ,  $_= '.' . chr ord
when using normal quotes.

This is a list consisting of two items:

'' . '.'  .   "$=" ^  '$' . $=
and
$_= '.' . chr ord
Lets look at the first part:

'' . '.' . "$=" ^ '$' . $=

This uses ^ as XOR, which somehow means (i'm not tha good at bitwise stuff)

that each bit on the left of ^ gets XOR'ed with each bit on the right, so:

'.$=' ^ '$60'
(where is the 60 coming from? $= or $FORMAT_LINES_PER_PAGE defaults to 60) which yields the linebrake used in the cascade.

Now, the second part:

$_= '.' . chr ord
This is very intuitive way to write
$_= '.'.$_
ord converts the contents of $_ into the numeric ascii value. Those values then get passed to chr, wich converts numeric asscii values to the corresponding character.

Adding the two parts together, we now have:

($_="\n",$_='.'.$_;)
Which can be further simplified to:
($_=".\n")

So the whole first line looks now like

for(;$_ ? undef : ($_=",\n") {

for(s .. Just another perl hacker.) {
Here you use a . as a regex-separator, so this looks like
for(s//Just another perl hacker/) {
which substitutes the first empty string in $_ with 'Just another perl hacker' and therefor is the same as
s//Just another perl hacker/;

for(;s;.;;;print){ }
remebering the for-syntax, you here again intialise nothing.
The continuination-test looks de-obfuscated like s/.//
which a) removes the first char of $_ (thus causing the cascading effect) and b) returns undef if $_ is empty, stopping the for-loop and instead of incrementing a counter (which is usually done here), you just print $_.
The loop itself does nothing;

So:

while (s/.//) {
      print $_;
}
We end up with:
while($_ ? undef : ($_=".\n")) {
    s//Just another perl hacker/;
    while (s/.//) {
        print;
    }
}
Now lets de-obfuscate the algorithm

The stuff inside the main for-loop is quite easey:

Put "Just another perl hacker" into $_, chop of the first char and print it.

The interesting stuff happens in the definintion of the first while loop

If $_ is defined, return undef. As $_ is not defined by default, in the first run, do the other part if the ? : statement, which sets $_ to "\n."

Now we enter the code in the while-loop, that adds "Just another perl hacker" in front of $_ and than prints the cascade.

That's it (I think ...)

Replies are listed 'Best First'.
Re: Re: foreach(silly..japh)
by Dog and Pony (Priest) on Mar 06, 2002 at 11:53 UTC
    Very nice. :) You almost got it all. However,

    $_= '.' . chr ord
    is not exactly the same thing as
    $_= '.'.$_
    Consider this:
    ${_} = q...q,.,.qq,$=,^q.$..$=; print ord() . "," foreach(split//);
    and then try to replace like this:
    
    for(;$_?$,:(${_} = q...q,.,.qq,$=,^q.$..$= => $_=q,.,.$_ );) {
    
    It destroys the output (or should, at least). This is because ord returns the value of the first character in the string it is fed. There is a difference. If you replace all the way down to $_="\n" then it works again, of course. :)

    For more clarity,

    for(s//Just another perl hacker/) {
    is really
    
    foreach(s//Just another perl hacker/) {
    
    - although they are interchangeable syntax-wise in perl.

    I don't know if you used it, but sadly deparsing gives a lot away. What I do think is funny is that deparsing translates one for to while, one to foreach and leaves the last as a for:

    while ($_ ? $, : ($_ = '.' . "$=" ^ '$' . $=, $_ = '.' . chr(ord $_))) + { foreach $_ (s// Just another perl hacker/) { (); } for (; s/.//; print $_) { (); } }
    Then again, I guess I am easily amused.

    I like your initial reminder too - that was exactly what this was "about" - abusing the for construct, which is also a bit special in perl. :)

    And oh, there is one more stripping possible to do - to get to approximately where I started (using your example):

    $_=".\n"; s//Just another perl hacker/; while (s/.//) { print; }
    Neither of the for loops has any real reason to be nested into any other - it is really just three separate lines of code.

    Thank you for taking the time - it is appreciated. :)


    You have moved into a dark place.
    It is pitch black. You are likely to be eaten by a grue.
      Well, even if i didn't get right 100%, really trying to understand what is happing in some obfuscated code is at least as amusing as writing it.

      And a lot more fun than just copy-and-past'ing the code to a file and letting it run ...

        I agree totally, and that is why I hoped you would appreciate that I did point out a few extras - after all, you did crack the "obfu", and I am not trying to nitpick the solution or bring it down in any other way. :)
        You have moved into a dark place.
        It is pitch black. You are likely to be eaten by a grue.