Шизоидный язык программирования самообучающихся алгоритмов «Автор»

в 15:42, , рубрики: C, c++, Автор, ИИ, основы, язык программирования, метки:

Я хочу поведать миру принципиально новый язык программирования, аналогов которому нет во всём мире.

В своё время я был, одержим идеей искусственного интеллекта. Когда я стал программистом, я понял, что всё не так просто, и можно сказать даже гораздо сложнее, чем казалось. Я не переставал работать над программами и осваивал все известные нынче языки программирования. Я пытался переложить на машину как можно больше собственных мыслительных процессов, и оказалось, что они занимали большие объемы кода и сами умели очень мало. Я пытался заставить программы учится, делать выводи, и использовать их в дальнейшем. Но все языки программирования имеют одинаковый недостаток – они не могут рассматривать алгоритмы, как данные. Программы не могут учиться, по тому, что они не имеют доступа к самим себе.

Так я пришел к тому, что нужно сделать «Си» подобный интерпретатор, программы на котором будут иметь возможность доступа к самим себе. Идея проста – чтобы учиться, нужно изменяться, а для этого нужно иметь доступ к собственному коду. Теперь обучение становится хотя бы возможным. После любого вмешательства в тело алгоритма, оно становится отличным от своего исходного кода программы и эти изменения нужно сохранить. Интерпретатор становится как бы автором новой программы. Поэтому язык использует следующую культуру. Файл с кодом «*.txt» имеет свой дубликат «*.code», который создаётся автоматически. Такая пара называется модулем. При загрузке модуля «Автор» выбирает, из пары, файл, записанный позже, а в случае отсутствия одного из пары, и выбирать не приходится. Таким образом, исходный код всегда остаётся нетронутым, а все изменения, которые произошли, будут видны программисту в файле дубликате. Стартовый модуль должен иметь точку входа (main) и для запуска нужно перетянуть мышкой файл на «author.exe». Любой модуль может запрашивать дозагрузку других модулей (#include <me.txt>). Также модули можно загружать и выгружать в процессе исполнения кода командой include(“me.txt”) и uninclude(“me.txt”). По завершении работы алгоритма, «Автор» выгружает стартовый модуль. Каждый модуль имеет счётчик количества модулей, которые его запросили, и в случае достижении нуля, модуль выгружает свой код в файл дубликат. До перезаписи файла дубликата модуля доходит только тогда, когда в нём (модуле) случилось хоть одно изменение или же дубликата не было вовсе. Таким образом, можно как угодно прописывать подключение модулей, не задумываясь о том, каким будет дерево подключений, модуль всегда будет в памяти в одном экземпляре. Главное не подключить самого себя.

Синтаксис языка «Си» подобный. Модуль содержит множество функций. В файлах программа представлена как текст. Но при загрузке, происходит преобразование и развитие кода до динамической структуры схемы алгоритма. Для каждой функции создаётся потоковый граф и уже к нему получает доступ программа. При выгрузке модуля происходит обратное преобразование, схемы в текст программы. «Автор» даже пытается придерживаться стиля программирования, чтоб код был не в одну строчку. Таким образом, можно, например, сделать функцию, которая сможет подсчитать количество условий или циклов в указанной функции, например в самой себе. Полагаю читателю будет интересно взглянуть на такую функцию, по этому, не смотря на запрет модератора, я приведу её:

// noproblem.txt
// Программа подсчёта собственных циклов и условий
void main(){
	f=getFunction(getThisFunctionName()); // доступ к себе
	tree=f.export(); // преобразовать в дерево алгоритма
	counterIF=0;
	counterWHILE=0;
	counterDO=0;
	counterFOR=0;
	// организация обхода дерева вложенных циклов и условий
	access={};
	do{
		n=tree.getRowSize(access);
		access.push(n);
		while(access.size()){
			n=access.pop();
			--n;
			if(n<0)continue;
			access.push(n);
			sub=tree.getSub(access);
			type="";
			if(typeof(sub)=="program")type=sub.typeof();
			if(type=="if")++counterIF;
			if(type=="while")++counterWHILE;
			if(type=="do")++counterDO;
			if(type=="for")++counterFOR;
			break;
			}
		}while(access.size());
	trace("В алгоритме функции " + f.getName().export() + " содержится:");
	trace("Условных ветвлений: "+counterIF);
	trace("Циклов while: "+counterWHILE);
	trace("Циклов do: "+counterDO);
	trace("Циклов for: "+counterFOR);
	getstring();
}

В алгоритме функции «main» содержится:
Условных ветвлений: 6
Циклов while: 1
Циклов do: 1
Циклов for: 0

Переменные в языке не имеют привязки к типу подобно PHP и JS. Их даже необязательно объявлять. Тип, указанный при объявлении, служит не более чем комментарий. Переменная создаётся также при употреблении её в конструкции для записи (а=0;). При обращении для чтения из неизвестной переменной возвращается тип «void».

Значение любой переменной может иметь тип: void, int, float, double, digit, char, string, interval, vector (множество), set (уникальное множество), map (ассоциативный массив), program (дерево алгоритма), function (схема алгоритма), graf, module. В языке также присутствуют указатели, но в связи с шизоидной особенностью, они организованы как строка с данными для доступа к переменной. Переменная может содержать указатель на саму себя (p=&p;p=****p;). Можно взять строку с названием типа значения. «typeof(typeof(#))==”string”» – всегда истина.

В языке можно использовать особый операнд «#». Он возвращает тип «void» и служит символическим обозначением скрытого блока, который, как и любой другой операнд, можно поменять на конструкцию дерева операторов, какой угодно сложности. При попадании значения «void» в условие тернарного оператора, интерпретатор, каждый раз, на момент исполнения, случайным образом определяется между истиной и ложею. Выражения «#?1:0» и «rand()%2?1:0» – аналогичны. Но есть серьезная разница между «if(#);» и «if(#?1:0);».

При попадании значения типа «void» в условие, наступает шизофрения. Текущая точка исполнения алгоритма, в этот момент, делится на две. Одна переходит по истине, другая по лжи. В дальнейшей интерпретации, по мере необходимости, делится на две и вся карта памяти. Таким образом, можно сказать, что каждая точка исполнения имеет свою отдельную карту памяти.

// Пример шизоидной программы:
main(){
	n=0;
	if(#)n=1+1;
	n+=10;
	trace(n);
}

10
12

Поскольку консоль никак не делим, вывод на него осуществляется по очереди в неопределённом порядке.

Деление точки исполнения можно сделать и особой функцией «rozpad()». Она принимает множество значений и возвращает каждое из них в своей точке исполнения. Таким образом, выражения «if(#);» и «if(rozpad({1,0}));» аналогичны. Выражение «n=rozpad({1,2,3})+rozpad({10,150});» причинит шизоидный распад на шесть процессов, с различным значением переменной «n»: 11, 12, 13, 151, 152 и 153.

Чтобы определится между всеми вариантами процессов случайным образом, нужно вызвать функцию «define();». Таким образом можно свести все точки исполнения в одну.

Пришло время сосредоточится на шизоидной особенности языка. Так я называю возможность обработки неоднозначных данных. Все современные языки программирования используют переменные для хранения разнообразных данных, но все их значения находятся в единственном варианте. «Автор» же позволяет хранить неоднозначные значения в любой переменной. Таким образом, алгоритмы написаны на этом языке, готовы в любой момент, столкнутся с возникновением неоднозначности в процессе решения поставленной задачи. Но, часто, бывает так, что сама задача, на человеческом языке звучит неоднозначно. Например, такая задача: «Дано массив из 4 – 5 чисел, нужно его отсортировать».
Неоднозначность алгоритма решения исходит из самой постановки задачи. Во-первых, не указан однозначно размер массива, а точнее размер его задан в двух вариантах. Допустим, мы можем сами определить значения чисел массивов. Во-вторых, отсортировать массив возможно как по возрастанию, так и по убыванию. Вот как выглядит программа для решения данной задачи:

// sort.txt
main(){
if(#)m={6,2,9,1}; else m={3,7,21,45,8};
m.sort();
if(#)m.reverse();
trace(«Результат: „+m.export());
}

Результат: {9,6,2,1}
Результат: {45,21,8,7,3}
Результат: {1,2,6,9}
Результат: {3,7,8,21,45}

Операнд «#» возвращает значение «void». При попадании его в условную конструкцию ветвления, из которой, между прочим, состоит любой цикл, точка исполнения алгоритма делится на две. Одна переходит по ветке истины, другая по ветке лжи. Для каждой точки исполнения существует своя карта памяти, то есть относительно одной позиции интерпретации, все переменные содержат однозначные значения. Таким образом, после исполнения первой строчки программы, компьютер как бы делится на два. Они оба продолжают исполнять одну и туже программу, но уже с разными значениями переменной «m». Во второй строчке программы, каждый компьютер сортирует свой массив по возрастанию. Далее происходит деление каждого компьютера ещё на два. Одна пара проходит через команду реверса массива, а другая – нет. Затем все компьютеры выдают отчёт на свой общий экран.

Теперь рассмотрим шизоидное деление на другом примере. Нужно найти, какая комбинация значений переменных «а» и «b» даст в сумме число 20, притом, что «а» может быть одним из множества {5,3,7}, «b» может быть одним из множества {15,17}.
Программист сразу увидит тут два вложенных цикла. А вот, как выглядит программа на «Автор»:

void main(){
a=rozpad({5,3,7});
b=rozpad({15,17});
if(a+b!=20)OFF;
trace(“a+b==»+a+"+"+b);
}

a+b==5+15
a+b==3+17

В первой строчке программы происходит деление точки исполнения на три параллельных, в каждой из которых функция «rozpad()» возвращает своё значение, одно из заданного множества. Во второй строчке происходит аналогичное деление каждой точки исполнения на две. Таким образом, переменные получат своё сочетание значений из указанных множеств. Далее идет условие, по которому все «компьютеры», у которых «a+b != 20» переходят к команде «OFF», по которой они исчезают. Таким образом, до команды отчёта дойдут только те «компьютеры», у которых «a+b == 20» и значение их переменных выводится на общий экран.
Часто приходится делать выбор и выбирать одну из альтернатив. Но для того, чтоб сделать любой выбор, нужно иметь один, чётко сформулированный, как угодно сложный, критерий. А как быть, если критерий выбора неизвестен или не задан? Случайный, равновероятный выбор. Вот универсальный и простейший критерий. Для указания в алгоритме потребности выбора одного варианта процесса, со своими данными, в языке есть встроенная функция/команда «define()». Она осуществляет выбор одной точки исполнения из существующего, на момент исполнения её, множества. Как это работает. Текущий процесс достигает команду «define()» и останавливается. Активным делается другой, не остановленный, процесс из списка параллельных процессов. Когда все процессы в списке стают, остановлены, это значит, что пришло время сделать выбор одного из них. Избирается одна точка исполнения из списка, а все остальные процессы закрываются. Выбранный процесс запускается на дальнейшее исполнение алгоритма. Таким образом, независимо от порядка обработки параллельных процессов, после исполнения команды «define()» объективно, гарантировано остаётся только одна точка исполнения, со своим вариантом данных.
main(){
trace(«Известные приветствия:»);
string str = rozpad( {«Привет.», «Здравствуйте.», «Доброго времени суток.»} );
trace(str);
define();
trace(«Я выбрал: „+str);
}

Известные приветствия:
Привет.
Здравствуйте.
Доброго времени суток.
Я выбрал: Привет.

Следующая программа определяет из заданного множества чисел одно отрицательное и одно положительное число:

void main(){
i=rozpad({-3,-2,-1,0,1,2,3});
if(i<0)define(); else define();
trace(i);
define(-1);
getstring(); // ждёт нажатия «Enter»
}

-3
1

В первой строчке кода происходит деление на варианты. Во второй – деление на две группы и определение одного варианта для каждой из условных групп. Далее выводим на экран результат и ждем нажатия «enter».
Функция «define()» может принимать одно числовое значение. Оно определяет приоритет определений. Первым произойдёт то определение, которое получит большее число. По умолчанию берётся ноль. Если порядок определения неважен, можно использовать одинаковое число. Вот пример кода, для которого порядок определения важен:

void main(){
int n=rozpad({-2,-1,0,1,2,3});
if(n>=0)define(1);
trace(n);
define(2);
trace(“OK»);
}

-2
-1
OK
2
OK

Продолжение следует.

Автор: monstr0518

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js