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

Я хочу поведать миру принципиально новый язык программирования, аналогов которому нет во всём мире.
В своё время я был, одержим идеей искусственного интеллекта. Когда я стал программистом, я понял, что всё не так просто, и можно сказать даже гораздо сложнее, чем казалось. Я не переставал работать над программами и осваивал все известные нынче языки программирования. Я пытался переложить на машину как можно больше собственных мыслительных процессов, и оказалось, что они занимали большие объемы кода и сами умели очень мало. Я пытался заставить программы учится, делать выводы, и использовать их в дальнейшем. Но все языки программирования имеют одинаковый недостаток – они не могут рассматривать алгоритмы, как данные. Программы не могут учиться, по тому, что они не имеют доступа к самим себе.
Так я пришел к тому, что нужно сделать «Си» подобный интерпретатор, программы на котором будут иметь возможность доступа к самим себе. Идея проста – чтобы учиться, нужно изменяться, а для этого нужно иметь доступ к собственному коду. Теперь обучение становится хотя бы возможным. После любого вмешательства в тело алгоритма, оно становится отличным от своего исходного кода программы и эти изменения нужно сохранить. Интерпретатор становится как бы автором новой программы. Поэтому язык использует следующую культуру. Файл с кодом «*.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
Следует продолжение.

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 29

    +6
    Было бы здорово убрать статью в черновики, ознакомиться вот с этим руководством, и переоформить её в соответствии с рекомендациями. Читать тяжело.
      +13
      Но все языки программирования имеют одинаковый недостаток – они не могут рассматривать алгоритмы, как данные. Программы не могут учиться, по тому, что они не имеют доступа к самим себе.

      Эти два тезиса, очевидно, ошибочны.
        +9
        Матерь Б-жья, вот это шиза.

        А вы точно осваивали все известные ныне языки программирования? А то у вас язык си-подобный, файлы TXT, AST-деревом и не пахнет, плюс всякие там rozpad.

        Я тут кстати только что на Ruby получил доступ к исходному коду исполняемого скрипта и распарсил его, получив AST. Дальше я могу его изменить, собрать заново и запустить, причем безо всякого rozpad'а. Что я делаю не так?
          0
          Любой алгоритм можно представить (преобразовать) в дерево вложенных условий и циклов.
            0
            Я вам больше скажу. Любой алгоритм можно преобразовать в поток инструкций с условными переходами. Компиляторы именно этим и занимаются. Но я подозреваю, что у вас нет алгоритма такого преобразования.
              0
              Перед запуском каждая функция проходит путь: текст -> дерево -> схема алгоритма. Последняя исполняется интерпретатором. А потом проходит обратный путь преобразований. Так, что изменения в схеме, будут видны в тексте программы.
          +11
          Lisp передаёт вам привет.
            –4
            Сходство проявляется только в том, что они оба интерпретаторы. К тому же Лисп язык низкого уровня.
              +3
              К тому же Лисп язык низкого уровня.

              Серьезно? И как вы это определяете?
                0
                ((1 2 +) 4 *) <==> (1+2)*4 неудобно :) а в больших программах просто не читабельно.
                  0
                  На уровень языка неудобство/нечитабельность никак не влияют. И да, неудобство — это вопрос вашей непривычки, а читабельность нарабывается построением символов более высокого уровня.

                  PS То, что вы написали, кстати, не лисп.
                0
                Лисп настолько низкого уровня, что ему требовалась специальная ЭВМ для запуска в своё время, ага…

                К сожалению, это всё очень похоже на «альтернативную теорию электромагнитизма» или «альтернативную теорию гравитации» и тп.
            +12
            Вы, видимо, плохо искали. Иначе обязательно наткнулись бы на lisp, который реализует принцип программа как данные. При этом, некоторыми он считается достаточно шизоидным. Так что выполняются оба ваших условия.
              +2
              На самом деле даже на Си никто, как я понимаю, не мешает хранить текст файлов на диске, менять их и вызывать компиляцию из командной строки некоторых из файлов. Придумывать для этого целый свой язык вообще не обязательно. На других языках, даже на Java/C# и т.п. легко генерить классы на лету. Так что проблема изменения исходного кода это самая простая из проблем.
                0
                Здесь я видимо должен уточнить. Интерпретатор позволяет алгоритму вмешиваться в себя, прямо в процессе работы.
                И ничего перекомпилировать ненужно. Алгоритмы могут анализировать себя как схему алгоритма а не как текст/код программы.
                +6
                Идея само-модифицирующихся программ далеко не нова, но очень интересна по своей сути.
                Статья у вас никудышная, но это вовсе не значит, что ваши идеи бесперспективны и их стоит забросить.
                Не обращайте внимание на минусы и продолжайте работать в этом направлении.
                Рано или поздно вы обязательно достигните успеха.
                  0
                  Не совсем согласен.
                  Да, работа хорошая и перспективная. Но. На критику обращать внимание обязательно нужно.
                  В данном случае прислушаться к советам изучить Лисп.
                  0
                  Я, например, давний любитель языка REXX, тоже весьма…
                    +4
                    Из всей статьи мне понравился оператор if(#), который выполняет обе ветви вычисления. В этом есть что-то от квантовой неопределенности. Если сосредоточиться на этой функции и разработать удобный синтаксис для таких дуальных вычислений, может что-то и получится интересное.
                      +1
                      Оператор хорош, да, но такую конструкцию вполне легко организовать и на имеющихся ЯП. Хотя, язык с такой штукой «из коробки» — весьма интересно. =)
                        0
                        Да еще с параллельным выполнением таких ветвей, и еще lazy execution всей этой лапши до момента «измерения состояния системы»
                          0
                          Насколько я понял из текста, параллельное исполнение у автора реализовано. Насчет ленивости… это круто, конечно, и если все равновероятно, то даже вроде бы понятно, как реализовать. Но если хочтся управлять вероятностью получить систему в определенном состоянии, то видимо все равно необходимо будет выполнить все ветви.
                        0
                        Насколько я слышал, такое по-умолчанию происходит в большинстве видеокарт для гомогенности исполнения кернелов в рамках одного варпа (ссылка по теме).
                          0
                          В Си и Unix это реализовано ещё с бородатых 70-х годов в виде функции fork().
                            –1
                            Не сечешь ты, брат
                          0
                          Крутой язык.
                            +4
                            Взглянул в исходный код, это сказка. Ребята, не судите строго человека, дайте ему продолжить.
                            monstr0518 постарайтесь проверить хотя бы грамматические ошибки в посте, и, конечно, пожалуйста, продолжайте.

                          Only users with full accounts can post comments. Log in, please.