Перевод текста в команды FALSE

После публикации статьи про FALSE мне пришла в голову интересная, хоть и бесполезная идея — реализация программы, которая составляет другие программы. Все было бы нормально, но в этот раз эта статья будет точно соответствовать одному из разделов нашего блога — разделу «Невменяемое программирование».

Если вы хотите немного причаститься к ненорммальному программированию, то добро пожаловать в этут статью !

Как я уже упоминал, в этот раз мы будем писать программу, которая генерирует другие программы, правда эти программы будут всего лишь простыми программами вывода определенных вами строк текста. Но, прежде, чем мы приступим к реализации, давайте разберемся, что собой представляет программа вывода на FALSE.

Как вы помните из предыдущей статьи, вывод в FALSE представлен двумя командами: вывод целого числа (в этом FALSE напоминает целочисленный калькулятор) и вывод одиночного символа (в этом FALSE выглядит как достаточно медленное устройство, выдающее данные). В данном случае, будем использовать команду вывода символа, которой необходимо, чтобы в стеке оказалось некоторое число, которое можно интерпретировать как код символа ASCII. Это обстоятельство сразу же наложит на программу-генератор жесткое ограничение — ей на вход можно будет подать строку содержащую только символы из ASCII.

Команда вывода сможет вывести только один символ, код которого лежит в стеке, однако, если в стек каким-то образом закинуть несколько кодов и применить ее столько раз, сколько необходимо, то можно будет распечатать на терминал интересующую нас строку. Если просто последовать такой логике, то достаточно строку из символов поэлементно обработать, превратив каждый из символов в код, то последовательность полученных чисел, указанных через пробел и есть искомая программа печати.
Но… Просто последовательность неких чисел не выглядит загадочно и не так интересно, кроме того, кто-нибудь может догадаться, что эта последовательность тут не просто так !

Поэтому, а также потому, что очевидных путей мы не ищем, я придумал кое-что получше…

Получив последовательность кодов, мы возьмем первый ее элемент как базис, перейдем к следующему элементу и посмотрим насколько он отличается от базиса — больше он или меньше, для чего вычтем элемент из базиса или базис из элемента, после этого назначим только что взятый элемент базисом и повторим операцию. Если мы продолжим дальше данные действия до конца массива кодов, то в итоге у нас будет получен набор «разниц», который объясняет как отличается каждый новый элемент от самого первого элемента массива.

Исходя из этого, нам нужно будет положить первый элемент массива (причем не важно в каком виде — числа или символа, несколько далее мы используем этот факт) и прибавить к нему «разницу», а затем просто повторить эту процедуру до окончания набора «разниц». Естественно, прибавление (или вычитание) — мы реализуем в виде команды FALSE.

Тогда, процедура создания программы довольно проста: в строку-накопитель помещаем символ апострофа, первый элемент массива, преобразованный в символ и строку $,[-$,]a:[+$,]b: (это небольшая программа на FALSE, которая копирует вершину стека, выводит ее как символ и добавляет две функции a и b, которые вычитают и прибавляют «разницу»), дальше же просто происходит накапливание «разницы» с функциями a/b и добавление команды очистки стека.

Это первый вариант проограммы на FALSE, который мы получим, простой, но еще недостаточно запутанный.

Следующий вариант, который можно осуществить — это преобразование «разницы» в символ, но не каждое число дает символ, который мы можем увидеть, так как ASCII коды с 0 до 33 являются управляющими символами. Воспользуемся этим фактом при преобразовании в символ — если число меньше 33, то добавим 33, преобразуем в символ, после чего вставим команду вычитания 33 из положенного в стек символа; в противном случае, просто выполним преобразование.

Для этого в строку наполнитель поместим смешанную функцию FALSE $,[‘!—$,]x: (команда продублирует верхушку стека, выведет ее как символ, а затем положит функцию x, которая поместит восклицательный знак (его код как раз 33), вычтет его из символа на стеке, а затем выполнит вычитание «разницы»). Дальше все, как и в предыдущем варианте — накопление строки и удаление последнего символа со стека.

Код алгоритма:

import std.algorithm;
import std.conv;
import std.math;
import std.range;
import std.string;
import std.stdio;

void main(string[] args)
{
	auto dataForConversion = args[1]
			.map!(a => a.to!char.to!int)
			.array;
								
	string flowProgram1, flowProgram2;

	writeln("Codes: ", dataForConversion);

	auto first = dataForConversion[0];
	
	flowProgram1 ~= "'" ~ first.to!char.to!string ~ "";
	flowProgram2 ~= first.to!string ~ "$,['!--$,]x:";

	foreach (number; dataForConversion[1..$])
	{
		if (first > number)
		{
			auto diff = first - number;
			flowProgram1 ~= diff.to!string ~ "a;!";
			if (diff < 33)
			{
				flowProgram2 ~= "'" ~ (diff + 33).to!char.to!string ~ "x;!";
			}
			else
			{
				flowProgram2 ~= "'" ~ diff.to!char.to!string ~ "-$,";
			}
		}
		else
		{
			auto diff = number - first;
			flowProgram1 ~= diff.to!string ~ "b;!";
			flowProgram2 ~= diff.to!string ~ "+$,";
		}

		first = number;
	}

	writeln("First variant: ", flowProgram1 ~ "%");
	writeln("Second variant: ", flowProgram2 ~ "%");
}

Испытаем, наша программа принимает как аргумент командной строки строку для преобразования, для примера запустим нашу программу с текстом «LightHouse Software»:

rdmd converter "LightHouse Software"

Результат:

Codes: [76, 105, 103, 104, 116, 72, 111, 117, 115, 101, 32, 83, 111, 102, 116, 119, 97, 114, 101]
First variant: 'L29b;!2a;!1b;!12b;!44a;!39b;!6b;!2a;!14a;!69a;!51b;!28b;!9a;!14b;!3b;!22a;!17b;!13a;!%
Second variant: 76$,['!--$,]x:29+$,'#x;!1+$,12+$,',-$,39+$,6+$,'#x;!'/x;!'E-$,51+$,28+$,'*x;!14+$,3+$,'7x;!17+$,'.x;!%

Вообще, создание таких программ-конвертеров — это тоже своеобразное искусство создания остроумных и креативных алгоритмов, и честно скажу, что можно нашу программу сделать еще более запутанной (или даже устроить целый конкурс на хитроумную программу-конвертер, что возможно мы когда-нибудь и сделаем), и я очень рад тому, что поучаствовал в данной области (это был хоть и интересный опыт, но по большей части — бесполезный).

Напоследок, я хотел бы вам сказать о том, что вы можете потренироваться в составлении программ на FALSE или испытать программы из конвертера в бесплатном и соткрытыми исходными кодами интерпретатором FALSE, который был создан нашей командой, а найти его вы можете здесь.

Добавить комментарий