Quine

This post is part of a series on Mohammad Anwar’s excellent Perl Weekly Challenge, where Perl and Raku hackers submit solutions to two different challenges every week. (It’s a lot of fun, if you’re into that sort of thing.)

Challenge #2 this week asks for “a script that dumps its own source code”. This is almost a quine, although Mohammad did not name it as such. Specifically, we are given the constraint that perl ch-2.pl | diff - ch-2.pl must return nothing.

What’s a Quine?

A quine, otherwise known as a self-replicating program, is a program that accepts no input, and produces a copy of its source code as its only output.

This is a stronger definition than what Mohammad has asked for this week: specifically, Mohammad did not add any restrictions on input. So, programs that simply read their own source file would be acceptable this week. Since I felt that was too easy, I have decided to go with the stronger definition of an actual quine, not allowing any input. Still, I’ll include a few solutions to show the various options:

The Cheaty Way

There are a lot of ways to look at quines. One obvious way is to simply read the script’s source and print it:

open my $fh, '<', __FILE__;    # Perl
print do { undef $/; <$fh> };
$*PROGRAM.lines».say           # Raku

However, the usual definition of a quine forbids any input, and reading the contents of a file certainly qualifies as input. However, the challenge description didn’t specifically ask for a quine by name, so maybe the cheat isn’t a cheat. However, I’m not letting myself off that easily.

The Even More Cheaty Way

Here’s a valid quine and it’s a polyglot:


No, no, please, hold your applause.

The Hard Way

The true challenge (and true beauty) of quines really starts to show through when you disallow all input, and trivial solutions like an empty file (above). Unfortunately, coming up with a novel way to write a quine in Perl after twenty-plus years is rather difficult. So I’ll instead point you towards The Quine Page, curated by Gary P. Thompson II (no relation). I’m going to use one of those.

You’ll see variants of the following construct in almost any discussion around Perl quines, and I’ve never been able to conclusively find out who really came up with the basic idea first. Gary lists V Vinay. That may be true, or V Vinay may have simply been the submitter. In any case, here it is:

$_=q<print"\$_=q<$_>;eval\n">;eval

Don’t trust my (or your) syntax highlighting of any of these examples. Quines exploit a lot of language features in strange ways, and Perl is hard enough to highlight at the best of times.

If someone knows (and, more importantly, can prove) conclusively who the original author of the above is, please let me know!

How does this work? Essentially, it uses string eval to evaluate the contents of $_, which are set to print"\$_=q<$_>;eval\n". This turns the usage of $_ inside the inner q<> into a recursive use, allowing the script to print its own contents. A good way to learn more is to start from here, and modify it yourself.

Not Satisfied

The above quine will work fine as-is. But the fun part about this particular quine is, the q<> quoting operator here allows us to add extra string content that will be executed as code, like so:

$_=q<"extra_stuff_here";print"\$_=q<$_>;eval\n">;eval

So I decided to have a little fun with it. If your terminal supports ANSI escape codes (which is pretty much any proper terminal), you can run my solution and see the following:

I <3 Perl

Raku

Raku supports string eval, too, as well as even more quoting operators and string manipulation, so coming up with a quine is even easier. This one is my own, although it’s so simple it’s entirely possible someone else beat me to it:

{.printf($_)}(<{.printf($_)}(<%s>)>)

Note that there is no trailing newline. I could have added newlines to the output, but I like the conciseness of this solution.

Sorry, no Raku ANSI art. I love Raku, too but, it’s just that one ANSI heart is about my limit in any given week.

Leave a Reply

Your email address will not be published. Required fields are marked *