Path: utzoo!utgpu!jarvis.csri.toronto.edu!cs.utexas.edu!usc!elroy.jpl.nasa.gov!jpl-devvax!lwall From: lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) Newsgroups: comp.lang.perl Subject: Re: Randal's one-liners Message-ID: <7282@jpl-devvax.JPL.NASA.GOV> Date: 6 Mar 90 01:48:46 GMT References: <15214@bfmny0.UU.NET> <15216@bfmny0.UU.NET> <7250@jpl-devvax.JPL.NASA.GOV> Reply-To: lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) Organization: Jet Propulsion Laboratory, Pasadena, CA Lines: 76 In article ehrlich@cs.psu.edu (Daniel Ehrlich) writes: : Could one of the perl gurus take a moment to enlighten a novitiate perl : hacker as to how or why any or all of these examples do what they do? Other : than that they all print out 'Just another Perl hacker,'. It's all magic. :-) Here's Tom's version, expanded and commented: @a=split(//,'1111211111131223311361214223121412311341121111222123616111111122'. '21233212615112114212321211121111222123321120'); # make array of chars foreach $r (@a) { # foreach digit above foreach $_ (1 .. $r) { # just count to $r: for ($_ = 1; $_ <= $r; $_++) vec($s,$i,1)=$v; # add a 0 or a 1 bit to string $s $i++; # increment bit location in bit vector } $v ^= 1; # flip between 0 and 1 using xor } print"$s\n"; # print the resultant string The primary thing you have to know is that vec() can set any arbitrary bit in a bitmap that happens to be held in a string, and Tom is just iterating over all the bits of the output string, setting the right number of 0 bits, 1 bits, 0 bits, 1 bits, etc. I confess it's so simple I had to stare at it a loooong time to figure it out... Here's my first version. The chief difference is that each character of our data array now encodes both the count of 0 bits and the count of 1 bits. The 0 bits are encoded in the lowest three bits, and the 1 bits are encoded in the next three bits. The whole character has 32 added to it to make it a printable character. Note that we don't have to worry about subtracting out the 32 when extracting the lower 3 bits. foreach $_ ( # make array of funny chars split(//,'))*))91:+9.*4:1A1+9,1))2*:..)))2*:31.-1)4131)1))2*:3)"') ) { foreach $_ (ord $_) { # funny way to say $_ = ord($_) $i += $_ & 7; # skip right amount of zero bits grep( # equivalent to a foreach vec($s,$i++,1)=1, # set the correct 1 bit 1 .. ($_ >> 3) - 4 # same as 1 .. (($_ - 32) >> 3) ); } } print"$s\n"; The last way is just an obfuscation of the previous one. It uses greps in place of the two outer foreach's, which is why it was especially for Randal, our chief grep nut. Note that the order of arguments changes when we switch to using grep. The other two changes are that, instead of subtracting 4 from the $_ >> 3, we just add it algebra-style to the left argument of .. and end up with a 5 there. And the print statement is tucked away there in the middle, ready to go off when the second three bits happen to equal 0, which the \7 on the end of the magic string provides. grep( # iterate over array of chars made by second argument do { # because grep can't take an sequence of statements directly for (ord) { # foreach $_ (ord $_) $i += $_ % 8; # same as $_ & 7 grep( vec($s,$i++,1)=1, 5 .. ($_ >> 3 || print "$s\n") ); } }, (@x = # assignment works around a perl bug! Eeek!! split(//,"))*))91:+9.*4:1A1+9,1))2*:..)))2*:31.-1)4131)1))2*:3)\7") ) ); Disgusting, ain't it? Well, you asked... Larry