Implementation notes Is that image black on white, or is it white on black? ---------------------------------------------------------------------- 0. Concept Writing programs can be thought of as replacing preexisting whitespaces with instruction codes. It's a perfectly reasonable way to write programs in general, because the reverse way of replacing code with whitespaces is generally less sensible. There is still a third way to write programs, where each character on screen is both whitespace and code. It takes a bit of convoluted thinking to know what this means, but you too can come to this state of mind by doing this exercise that I did: - Write a program, call it X. - Replace all whitespaces in X with characters, and replace all characters with whitespaces. Call this second program Y. - Tweak both X and Y until they have the same function. This is an interesting problem because every character you add to X is a character taken from Y, and vice versa. This is easy for small programs with trivial functions. I want to know how hard it would be to write interesting programs in this style. ---------------------------------------------------------------------- 1. Design Three ideas came together in doing this exercise: - The two programs should reproduce each other when executed. This folds the two programs into one. Any other function with the two programs being independent is not as interesting for this exercise. - The two programs should use opposite languages. Specifically, the two languages will be Perl and Python. Python is almost, but not quite, entirely unlike Perl. - The two programs should be formatted to resemble Lily from Touhou series. The only character that has an opposite image from the series. Lily White is first seen in Touhou Youyoumu, and Lily Black can be seen in Touhou Kaezuka. No official name for "Lily Black", actually, just that she wears a black costume instead of white. Anyways... ---------------------------------------------------------------------- 2. Perl quine in Perl I have done this quite a few times, and every time I managed to make the core program smaller. It works like this: - Expand template to a string with space+character+newline. - For each character, replace it with a character from code. - Write the code to perform the above two functions. lily*.pl implements these functions. ---------------------------------------------------------------------- 3. Python quine in Python I have heard some little kids saying how it's impossible to write unreadable code in Python. This is of course not true -- it's possible to write unreadable code in any language, especially Python. The Python designers have implemented some stylistic constraints in the language, such that most usual programs meet some expectation about where newlines are and how indents look. At the same time, they also left in the lambda backdoor. Hence the "especially" Python bit -- if you want to diverge from the expected style, it's possible to do it with lambda, and the result would be much more unreadable. Had the language been designed to accommodate multiple spacing styles, there would be no expectations to be ruined. Anyways, the Python quine has the same function as the Perl one, but I had to do some contortions around the syntax: - The simplest output function, "print", requires a newline. I don't want any extra newlines. This means I can have just one "print" in the program. That's okay, I can build one string and just print that. - Defining a function or variable requires a newline. I don't want any extra newlines. Fortunately there is lambda, which let's me define and reuse variables in the same line. - Defining a conditional requires a newline. I don't want any extra newlines. Since the conditional results can be evaluated without side effects, this is worked around by indexing an array with the conditional expression instead. - Defining a loop requires a newline. I don't want any extra newlines. There are a few ways to workaround this, list comprehension is one of them, built-in functions like "filter" is another. - Defining two loops requires two newlines. I don't... really, this can be elegantly implemented with coroutines, but Python's generators requires two newlines to define. The final workaround that didn't use any extra newlines was to use list comprehension for the outer loop (template), translating that index for the inner loop (code). This index translation function is done by counting non-whitespace characters in the template, so the Python code runs in quadratic time instead of linear time. lily*.py implements these workarounds. ---------------------------------------------------------------------- 4. Perl/Python quine in Python/Perl Having gotten Perl and Python code to produce formatted code, what remains here is merely an exercise in quoting the various strings. data_* are the individual unquoted strings, and generate_* are the scripts that assembles them together. This bit is really straightforward if you stare at the generate_* scripts long enough, the only part that might be a bit strange is the removal of the final newline from the template. This is needed to compensate for Python's implicit newline in "print" statement. For Perl, an explicit newline is inserted at the end of output. ---------------------------------------------------------------------- 5. Fitting it all together If the programs were to fit in a checkerboard pattern with equal amounts of whitespace and non-whitespace characters, this wouldn't be much of an exercise. An example of this can be found under v1/* However, if you examine the base image (template/lily.png), you will find white pixels outnumbering black pixels by almost exactly a factor of 2. This means the Python version requires twice as much code as the Perl version. This is done by duplicating some strings in the output, as seen in generate_python.pl. After adjusting the template and making sure that everything fits and runs, we get a fairly decent pair of programs. This version is found under v2/* The previous version would have been almost final, but note that there are some excessive semicolons in lily_white.pl near the end of the code. This is rather undesirable, so I rearranged the code a bit more to reduce the semicolons at the end. The result of this is the final lily_white.pl and lily_black.py This last bit took quite a bit of patience -- changing one character of code results in 2-4 changes elsewhere, and changing one character of the template results in 4-6 changes elsewhere. Only some balance of code and whitespace changes results in both programs working, and realizing how this worked was most enlightening. ---------------------------------------------------------------------- 6. Testing There are only a finite number of outputs, so this was rather easy to check. test.sh is used that there are only two outputs, and different combinations of python+perl will get to only one of the two outputs. ---------------------------------------------------------------------- 7. Finally... Lily is a fairy that announces the coming of spring. It's really cold around here these days, we could really use some spring weather...