SOLUTION

Disclaimer
----------

Cet assombrissement est soumis au nom de Paris.pm canal assombri.

Written by Jean Forget
Ponder.Stibbons@wanadoo.fr

Introduction
------------

APL is the greatest regexp-less programming language ever!

A long time ago, when Perl did not exist, I had access to an APL interpreter.
I had written a 18-char program which could write the list of the prime integers
lower than or equal to a given number N. This program (in pseudo-HTML and
with much whitespace) was:

char count 12 3 45 6 7 8 9        01 23 4 5 6           7      8
program    (2 = +/ 0 = V &circle; .| V) / V &leftarrow; &iota; N

The present obfuscated Perl program is a remote cousin of this APL program.

First, I describe the APL ancestor.
-----------------------------------

&iota; N is the same as Perl 1..$N (BTW, APL indices are 1-based by default).
&leftarrow; is the assignment in APL
With two vectors A and B and an operation "op", the expression "A &circle; . op B"
builds a rectangular array, each element being "A[i] op B[j]".
In this case, the vertical bar is the modulus. With N = 6, we would
have :
   1 2 3 4 5 6
1  0 1 1 1 1 1
2  0 0 2 2 2 2
3  0 1 0 3 3 3
4  0 0 1 0 4 4
5  0 1 2 1 0 5
6  0 0 0 2 1 0

You can compare a scalar value with a rectangular (or linear, or 3-D or higher) array,
APL automatically does what Perl's "map" would do. Therefore "0 = ..." would give
the array
   1 2 3 4 5 6
1  1 0 0 0 0 0
2  1 1 0 0 0 0
3  1 0 1 0 0 0
4  1 1 0 1 0 0
5  1 0 0 0 1 0
6  1 1 1 0 0 1
This array of booleans can be interpreted as the relation "line-number is a 
multiple of column_number", or as "column_number is a divisor of line_number".

"op / rectangular_array" creates a linear array (i.e. with 1 dimension fewer),
each element containing the result of the operator applied to all the elements of
a line. This would give :
1  1+0+0+0+0+0
2  1+1+0+0+0+0
3  1+0+1+0+0+0
4  1+1+0+1+0+0
5  1+0+0+0+1+0
6  1+1+1+0+0+1
that is:
subscript 1 2 3 4 5 6
value     1 2 2 3 2 3
which can be interpreted as the number of divisors for each integer.

Another implicit map is executed with the comparison "2 = N", and the resulting
boolean vector can be interpreted as "the number of divisors of N is 2", or
the equivalent but more elegant formula : "N is a prime number".
subscript 1 2 3 4 5 6
value     0 1 1 0 1 0

Lastly, "boolean vector / vector" is more or less the same a Perl's "grep".
0 1 1 0 1 0 / 1 2 3 4 5 6 gives 2 3 5

By the way, in APL, "/" is *not* the division, just as "=" is not the assignment,
but the comparison operator. The division is a composite
char built with ":" and "-".

And then, the Perl version.
---------------------------

As above, I suppose $ARGV[0] == 6.

The Perl version does not use the modulus operator. I build directly the
boolean array "is a multiple of". Additionally, this boolean array
is not a list of lists, but a single scalar string. This line contains
N-squared chars, each one "0" or "1". There is not array-line delimiter. 
We reach a new array-line every Nth char, that's all.

That is, the array
   1 2 3 4 5 6
1  1 0 0 0 0 0
2  1 1 0 0 0 0
3  1 0 1 0 0 0
4  1 1 0 1 0 0
5  1 0 0 0 1 0
6  1 1 1 0 0 1
is actually a string 100000110000101000110100100010111001

All numbers are initialized to zero (is not a multiple of). Then
a loop is performed to mark all multiples of 1, then all multiples of
2, then all multiples of 3, etc.

Marking all multiples of 1: in each line, change the first char to '1'
Marking all multiples of 2: in every other line, change the second char to '1'
Marking all multiples of 3: in every third line, change the third char to '1'

These actions are performed with regexp substitutions:
(1) s/().(.....)/${1}1$2/g
do not touch 0 lines plus 0 chars, change the next char to '1' (first char of 
every line), and leave 5 chars
(2) s/(.......).(....)/${1}1$2/g
do not touch 1 line plus 1 char, change the next char (the 2nd of every other line) 
to '1' and do not touch the remainding of the line (4 chars)
(3) s/(..............).(...)/${1}1$2/g
do not touch 2 lines plus 2 chars, change the next char (the 3rd of every third line) 
to '1' and do not touch the remainding of the line (3 chars)

Tracing this string through the loop would give:
000000000000000000000000000000000000
100000100000100000100000100000100000
100000110000100000110000100000110000
100000110000101000110000100000111000
100000110000101000110100100000111000
100000110000101000110100100010111000
100000110000101000110100100010111001

***But***
using ${1}1$2 as a substitution string would not have been obfuscated enough. 
So I first change the char to '(', and then from '(' to '1', using "y/(/1/". 
"y/(/1/" is too much readable, so I chose another separator, namely the 
closing parenthesis, which gives a pretty "y)()1)".
In the same way, the "s///" is written "s...", using the dot as a separator.

For the various substitutions (1), (2), (3) etc, the regexp to match is altered
at the end of each iteration, using another substitution, which replaces ").(."
by ".......).(" (that is, one more line and one more char in the first capturing
parentheses, and one less char in the second capturing parentheses).
When this substitution fails, that means that you have to leave the loop.

In addition to those substitutions, a few actions are performed, preparing
the next step.

How many divisors for each number:
the string 100000110000101000110100100010111001 is changed to
+1+0+0+0+0+0+1+1+0+0+0+0+1+0+1+0+0+0+1+1+0+1+0+0+1+0+0+0+1+0+1+1+1+0+0+1
and then unpacked into six 12-char chunks:
+1+0+0+0+0+0
+1+1+0+0+0+0
+1+0+1+0+0+0
+1+1+0+1+0+0
+1+0+0+0+1+0
+1+1+1+0+0+1
This is similar to APL's "+/", which produces and computes 1+0+0+0+0+0, 1+1+0+0+0+0 etc,
except for the leading pluses. But these leading pluses do not hurt, and
therefore "if it ain't broke, don't fix it".

Each chunk is evaled, and compared to 2. If the test is successful, the program
updates $_ to the proper value (the prime number, not how many divisors it has)
so it will be released by grep. This update is necessary because grep extract 
values from a list, not the values' indexes. We add the output record separator, 
so the result will be easier to read.

Remark.
-------

A buggy version of the APL program appears in the "Jargon File", entry "One-liner wars".
This entry also prints a Perl script which produces the list of prime numbers, but
this script closely derives from the Cookbook recipe 6.16, and printed in TPJ-8
page 52. This and my scripts produce the same result (how fortunate!), but work
completely differently.

