Self-reproducing Spiral

Tom Phoenix rb-pdx-pm at redcat.com
Tue Jul 16 00:18:18 CDT 2002


On Mon, 15 Jul 2002, Tyler F. Creelan wrote:

> "This self-reproducing code prints a turning spiral filled with it's own
> code."

Well, the whole spiral is just a single-quoted string. Just after that
string is assigned to $_, the code at the bottom is the engine:

    s;\s;;g;eval

The engine strips out the whitespace and evals that code. (The eval
operator, used like this, compiles and runs whatever Perl code is found in
$_. Using eval on a string is a bad idea in general, unless you totally
understand why it's a bad idea.)

Of course, that code looks like this without the whitespace:

    $q="\47";while($;=$z+=.5){%c=$r=0;$/="";while(20+$z>($;+=.05)){$c{int$_+26+2*($r+=.02)*sin$;}{1-$_+10+int$r*cos$;}=1for(0..1)}$t=reverse;$/.=`clear`."#!/usr/bin/perl\n\$_=$q\n";for$y(1..20){$c{$_}{$y}?$/.=chop$t:($/.="\40")for(0..53);$/.="\n"}print"$/$q;s;".chr(92)."s;;g;eval\n"}

So I'll put the whitespace back, in the right places, by using B::Deparse
with a command like this: perl -MO=Deparse source >deparsed , then I'll
fix up one extra long line to get this:

    $q = q['];
    while ($; = $z += 0.5) {
	%c = ($r = 0);
	$/ = '';
	while (20 + $z > ($; += 0.05)) {
	    foreach $_ (0 .. 1) {
		$c
		  {int $_ + 26 + 2 * ($r += 0.02) * sin($;)}
		  {1 - $_ + 10 + int($r * cos($;))}
		      = 1;
	    }
	}
	$t = reverse;
	$/ .= `clear` . "#!/usr/bin/perl\n\$_=$q\n";
	foreach $y (1 .. 20) {
	    foreach $_ (0 .. 53) {
		$c{$_}{$y} ? ($/ .= chop $t) : ($/ .= ' ');
	    }
	    $/ .= "\n";
	}
	print "$/$q;s;" . '\\' . "s;;g;eval\n";
    }

Here, let me annotate that:

    $q = q['];		# A single quote. The actual source uses
    			# neither single quotes nor whitespace.
    while ($; = $z += 0.5) {
            # Although $; is one of Perl's internal variables, it's
	    # being used here as an ordinary one. Pretend it's
	    # $semi if you'd rather. It is an angle expressed in
	    # radians. Of course, the while loop is a decoy; it's
	    # an infinite loop. This program runs until you break
	    # out of it. $z is an initial rotation parameter.
	%c = ($r = 0);
	    # This is erasing the contents of %c, although it's an
	    # odd way to do it. %c is being used to hold a picture of
	    # the spiral. (Not a normal use of a hash!) The value of
	    # $c{$x}{$y} will be true if the spiral intersects
	    # position $x, $y. $r is the radius of the spiral.
	    # $x ranges from 0 to 53, $y from 1 to 20.
	$/ = '';
	    # This is another internal variable being used for the
	    # author's own purposes. This one is collecting the
	    # output as one big multi-line string.
	while (20 + $z > ($; += 0.05)) {
	    foreach $_ (0 .. 1) {
		$c
		  {int $_ + 26 + 2 * ($r += 0.02) * sin($;)}
		      # That's the X-coord in the spiral picture. The
		      # constant 26 is half of the width of the
		      # spiral, roughly.
		  {1 - $_ + 10 + int($r * cos($;))}
		      # That's the Y-coord; 10 is half of the height.
		      = 1;
	    }
	}
	$t = reverse;
	    # That reverses $_ (which holds this program's source,
	    # remember?) storing the resulting string into $t. It's
	    # being reversed so that chop() will remove characters
	    # in the proper order.
	$/ .= `clear` . "#!/usr/bin/perl\n\$_=$q\n";
	    # The output of clear (external command being run in
	    # backticks) is your system's "clear screen" code.
	foreach $y (1 .. 20) {
	    foreach $_ (0 .. 53) {
		$c{$_}{$y} ? ($/ .= chop $t) : ($/ .= ' ');
		    # Either a space or some code, depending upon
		    # the spiral table, %c. It would be better,
		    # if you must use the ternary op, to write
		    # that like this:
		    #    $/ .= $c{$_}{$y} ? chop $t : ' '
	    }
	    $/ .= "\n";
	}
	print "$/$q;s;" . '\\' . "s;;g;eval\n";
	    # That outputs the engine at the bottom, spoiling
	    # the look of the spiral.
    }

The author of this code missed a number of opportunites to improve it
(although whether my changes would be improvements is a matter of debate).
In fact, a number of features make me wonder whether the author wrote it
originally for Perl 4, working around the lack of references by using
something like "$_!$y" as a hash key. (As far as I can see, this code
should work fine in Perl 4 with that mod. But I don't keep Perl 4 around
for testing.)

If I've missed anything, I hope someone else will jump in. Hope this
helps!

--Tom

TIMTOWTDI



More information about the Pdx-pm-list mailing list