in reply to rusty lock
Taking the code inside and cleaning up the formatting a little gives us a lot of mapping:s,$/,,g, eval
Using map changes the order of execution - operations occur from the inside out.$|--; print join $", map { $_[$_{$_}] = sub { join $,, map { $_ = (ord) - ( $_[0] ? ($_[0]+4+$=)*$= : $= ); $_ += (2+$=)*2 while $_ < (1<<$.)-2-3*$=; chr } split $,, pop } -> ($_{$_}, $_) } map { $_{$_} = $a++; $.++; $_ } map { y; ;;d; $= /= 1.5; $.++; $_ } sub { split $/, join q,,, <DATA>; unshift @_, map { $. = y---c - $|--; $_ } shift; { undef @;; map @{[ push @{ $;[ ord((/.{$.}(.)/)[$|]) ] }, $_ ]},@_; @_ = map @$_, @;; $.-- && redo } @_[$|--..$^F+$|] } -> ()
$|--;
This sets $| to 1 (using the special behaviour of $|).
The next section of code that executes is the innermost anonymous sub:
split "\n", join '', <DATA>;
Here we pick up the words that are at the end of the file, using split to fill @_. This is equivalent to @_ = <DATA>; chomp @_; or even just @_ = qw(uhinbyl zobxiiv xqsauh zozmib xtbemz ujwq xzaf ufde); to completely avoid the use of DATA.
unshift @_, map { $. = (length) - $|--; $_ } shift;
The code block in map returns $_, and unshifts back onto @_ the same value it shifted off. So here map is only operating on one value, and only running for its side effects. In this case, $. gets set to 6, the length of "uhinbyl" - 1, and $| gets flipped back to 0.
Let's break this down piece by piece:
$.-- && redo turns this block into a loop. Since $. was set to 6 earlier, the loop index goes from 6 down to 0. What's actually being done in the loop? Well, the regex /.{$.}(.)/ matches n characters, then the next character is returned because of the capturing parens. $| is now 0, so (/.{$.}(.)/)[0] is just a fancy way of saying substr($_, $., 1). It does behave slightly differently in the case where $. is beyond the end of the string - substr spits out a warning, but in this construct the regex fails to match and element 0 of an empty list is just more nothing. Converted to a scalar for ord, it becomes undef, and ord(undef) returns 0.
So, what ord returns is the code for the character at a given position. map here is in void context, so we are throwing away the return value from push. So it's equivalent to push @{ ... }, $_ for @_;. The push uses the output from ord as the index to an array (@; in the original code, which I have renamed to @bins here.) That array position becomes an arrayref, which will hold everything that maps to that ordinal.
So the first pass through, when $. is 6:
$bins[ ord('l') ] = [ 'uhinbyl' ]; $bins[ ord('v') ] = [ 'zobxiiv' ]; $bins[ 0 ] = [ 'xqsauh', 'zozmib', 'xtbemz', 'ujwq', 'xzaf', 'ufde' ];
The rest of the code is less interesting:
@_[$|--..$^F+$|]
$|-- returns 0 (the old value of $|) and flips it back to 1. $^F is 2, which added to $| - which just got flipped to 1 - yields 3. So this is actually @_[0..3], or a slice of the first 4 elements of @_. Yes, the other elements in DATA are thrown away - they are only there to misdirect. We end up with qw(ufde uhinbyl ujwq xqsauh).
The next two maps return $_, so they are again just doing side-effects:
Then:
And finally
So the sub gets executed four times, putting the results into @_:
$_[0] = sub { ... } -> (0, 'ufde') $_[1] = sub { ... } -> (1, 'uhinbyl') $_[2] = sub { ... } -> (2, 'ujwq') $_[3] = sub { ... } -> (3, 'xqsauh')
join '', map { $_ = (ord) - 11 * ( $_[0] ? ($_[0]+15) : 1 ); $_ += 26 while $_ < 93; chr }
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re^2: rusty lock
by jynx (Priest) on May 27, 2006 at 05:44 UTC |