Машина Кузьмина. (Управляемая недетерминированная машина Тьюринга.)
Раcсчитывая что читатель знаком с МТ оформим описание останавливаясь на отличиях предлагаемой машины от МТ.
Машина Кузьмина представляет собой перечислимое множество ячеек (концептов) и перечислимое множество управляющих устройств (можно считать их головками, кому это удобно) назначение которых – адресация (активация) концептов. Номер концепта будем называть адресом.
Ячейка (а не головка как у МТ) имеет множество состояний S (возможно бесконечное). Пока событием будем называть состояние концепта. Это не противоречит интуитивному представлению о событии как о процессе изменения состояния потому что факт изменения состояния определяется анализом на принадлежность новому состоянию (истинность события), и зачастую событие имеет имя этого нового состояния.
Каждое событие может иметь конечное множество подписок. Подписка — номер концепта к которому осуществляется переход управляющего устройства и при условии истинности события, которому принадлежит подписка.
Работа машина заключается в адресации (активации) концепта. При активации концепта, запускается анализ на истинность всех событий концепта (анализ не изменяет состояние концепта и потому может выполняться одновременно для всех событий) имеющих подписки, и при истинности этих событий машина активирует концепты с номерами, указанными в подписках (одновременно, если истинных событий с подписками больше одного). При отсутствии подписок адресация выполняет предназначенную функцию в зависимости от вида адресации (чтение-запись-выполнение-инициализация). На чем работа и заканчивается.
Виды адресации.
Адресация имеет 4 вида:
1. Адресация для чтения. Читает значение концепта. Генерирует событие «Чтение»
2. Адресация для записи. Записывает новое значение. Генерирует событие «Запись»
3. Адресация для выполнения. Генерирует событие «Выполнение»
4. Адресация для инициализации. Генерирует события «Инициализация» и «Выполнение».
Архитектура компьютера.
Реализация машины представляет собой почти классическую архитектуру с несколькими шинами адресации, и несколькими процессорами. Шины адресации реализуют кроме классических адресаций для чтения/записи/выполнение еще и инициализацию. Т.е. создание новых концептов. Шины адресации перед активацией, формируют собственный стек для параметров и динамических переменных. Каждый процессор при запуске формирует собственный стек, и имеет собственные регистры. Память приставлена не массивом ячеек стандартного размера, а множеством концептов единой структуры. Графически содержимое такой машины можно представить графом с концептами-вершинами и ребрами-подписками.
События.
Из предложенного выше определения события почти очевидно, что не каждое состояние имеет смысл объявлять событием. Опять же интуитивно, состояния, объявляемые событиями должны представлять семантический интерес. Т.е. в общем случае событие e - это некое подмножество состояний S представляющий семантический смысл. Событие e {s|s ∈ S}. Конечное множество событий E характеризирующих концепт назовем множеством событий концепта. Анализ на истинность события e заключается в проверке текущего состояния концепта на принадлежность множеству e.
Объявление события перечислением.
Объявление событий эквивалентно определению множеств состояний. Перечислимые типы таким образом перечислением значений одновременно определяют и одноименные события. Перечислимые типы определяются группой концептов, имя каждого из группы представляет концепт в группе и хранится как индекс этого концепта в группе. Если концепты в группе –значения, то имя концепта имеет еще два смысла. В операторах как значение концепта с индексом, а при формировании подписки обозначают событие, заключающееся в равенстве переменной этому значению. Рассмотрим на примере определения типа Boolean.
Boolean
Определим тип Boolean как именованную группу (имя группы непосредственно после открывающей скобки без пробела) из двух концептов типа токен (4 разряда) с именами "!" и "¬" и значениями 0 и 1:
{Boolean Token {"!" 0 #True.» "¬" 1 #False. »}}
Теперь для переменной F определенной ниже с начальным значением -!
Boolean F ! #Определение переменной типа Boolean и присвоение значения Истина. »
Формирование подписки на событие True.
F ! ((a +b) (F= ¬)) #По значению истина складываем a +b. И присваиваем значение False.»
Аналог оператора If:
F {! a+b ¬ a-b) #По значению истина складываем a+b. Иначе a-b.»
Знаки «!» и « ¬»являются значениями в операторе и событием.
Так же с помощью группы определим события, заложенные в архитектуре машины.
Определение событий адресации. Класс «|».
Event "|" Token { "←" 0 #Запись» "→" 1 #Чтение» "|" 2 # Выполнение» ":" 3 #Инициализация» }
Compare
{Compare Token { "=" 0 #Равно»">" 1 #Больше.» "<" 2 #Меньше.» }}
Определение событий данных. Класс «:».
Event ":" Token {"_" 0 #Отсутствует значение.» "=" 1 # Ноль» ">" 2 # Положительное.»
"<" 3 #Отрицательное.» Even 4 #Четное» Uneven 5 #Не четное» "~" 6 #Изменение значения.» }
События, определяемые перечислимыми типами, определяют синтаксис объявления событий, и это первый шаг в понимании.
Объявление событий (подмножеств) Compare.
Определение множеств состояний выполняется классом «~» (Сравнение) имеющий значение типа Compare.
Event "~" #Сравнение. Результат типа Compare. » | :{ Type " " #Операнд 1» Type "," #Операнд 2» Compare ";" _ Boolean "?" ! #Инверсия результата события»}
Пример концепта E. Определение событий EQ, More и Less.
Concept E {
Int32 (a, b) #Определение атрибутов a, b»
~ EQ a, b = #Определение события EQ.»
~ More a, b> #Определение события More.»
~ Less a, b < #Определение события Less.»
}
Итак, мы уже умеем стоить элементарные события. Но есть возможность строить и формулы из элементарных событий используя класс * (Define) применяя правила выполнения групп в событиях (круглые, и фигурные скобки объединяют элементы группы по правилу And, а квадратные по правилу Or). Отрицание события определяется Метаатрибутом (событием)- типа Boolean именем «?» и по умолчанию равным !. Метасобытие ? виртуальное событие –результат выполнения любого концепта. Фактически событие ? ¬ это аналог привычного события Error.
Пример концепта E. Определение событий NotEQ, NoMore, NoLess и LessOrNull .
Concept E {
Int32 (a, b) #Определение атрибутов a, b»
~ NotEQ a, b = ? ¬ #Определение события NotEQ.»
~ MoMore a, b> ? ¬ #Определение события NoMore.»
~ NoLess a, b < ? ¬ #Определение события NoLess.»
Define LessOrNull [~ a, b < a : = b: =] #Событие a<b или a=0 или b=0»
}
Определение события как Define не случайно. Ведь определение события фактически является выражением и представляет собой некоторое высказывание, а определение истинности события не что иное как определение истинности этого высказывания. Т.е. вполне логично определять таким образом понятия, истинность которых определяется по правилам логического разбора скобок и можно считать интерпретацией как в языке Prolog. Но, еще очевидней что формирование подписок выполняет роль оператора If в императивном программировании. Что ж мы тут нового предлагаем?
В чем отличие?
А отличие становиться заметным, когда мы обращаем внимание на то, что у нас много процессоров и шин адресации. В классической архитектуре программа выполняется так, как пишется. Последовательность операторов О1, О2…. Оn. которые изменяют состояние. Затем оператор If- анализирующий текущее состояние и переход в зависимости от текущего состояния. У нас же переход осуществляется непосредственно в момент изменения состояния. И последовательность выполнения операторов зависит не от их порядка в тексте, а в порядке указанном парой событие – подписка. Т.е. обычную программу можно представить в виде алгортима (или блок-схемы) выполняющейся пошагово.
Работу предложенной машины можно представить алгоритмом только по выполнению какой-то ветки где порядок определен последовательностью подписок событий оказавшимися истинными по факту выполнения.
https://ru.wikipedia.org/wiki/Алгоритм
В общем случае одновременно могут происходить переходы по нескольким подпискам по нескольким событиям, что порождает недетерминизм и двусмысленность в определении алгоритма как порядка или последовательности выполнения. О каком порядке и последовательности может идти речь при ОДНОВРЕМЕННОМ процессе. В общем случае МК гарантирует только порядок выполнения пары событие-подписка и назовем такую модель А-ритмом.
Пример.
Class ElectricMeter #Класс с параметрами времени в миллисекундах и счетчик суммирования»
|| # Конструктор» | @ Byte ( Interval _ # В миллисекундах одного тика.», Count 100 # Количество тиков для суммирования. По умолчанию 100») ' Параметры
{ InitCount = Count ' Присваиваем начальное значение счетчика
Timer ( Interval, Tick ) # Создание таймера, создание подписки.» }
|| #Создание динамических свойств» :
{ Byte { InitCount _ #Начальное значение счетчика»
Counter _ #Счетчик»
|| #Инициализация» | =InitCount #Количество тиков таймера до запуска вычислений. Присваивается при инициализации»
|| ← - = 1 #Ведем счетчик. При каждой записи вычитаем 1»
:: = [ ( Counter =_ ) Value =( Voltage/InitCount )* ( Electricity/InitCount )] #При событии 0, формируем счет и восстанавливаем значение счетчика. Как произведение средних значений тока и напряжения.»
}
' Определение атрибутов Voltage, Electricity и Value
Float { Voltage 0
|| ←@ Float X _ +=X # Суммирование значения при событии Write»
|| → = 0 # При чтении обнуляем счет.»
Electricity 0
|| ← @ Float X_ += X # Суммирование значения при событии Write»
|| → = 0 # При чтении обнуляем счет. »
Value 0 'Начальное значение
|| ← @ Float X_ + =X #Значение счетчика наращиваем при записи»
}
Void Tick _
|| | [ Counter =0 Voltage= "P1" Electricity ="P0"] ' Значение счетчика уменьшаем на 1 при записи значения 0 и читаем значения тока и напряжения с портов P0 и P1
}
В примере присутствуют события данных адресации «|» и класса данных «:» с именами соответственно «||» и «::»
Принцип работы счетчика.
Значения тока и напряжения (Voltage и Electricity) считываются с портов Р1 и Р0 как можно чаще. Не реже 5000 раз в сек. Считанные значения усредняются для устранения ошибок суммируя эти значения Count раз и деления на значения счетчика усреднения Count.
Расчет значения счетчика электроэнергии происходи суммированием произведений усредненных значений в атрибуте Value.
Работа программы:
Новый объект счетчика создается из класса адресацией с инициализацией к классу. (машинная команда Go с параметром |, либо синтаксическая форма New). Адресация с инициализацией формирует (машиной) одновременно адресацию для выполнения. Потому происходит сначала формирование концепта (подписка на событие «инициализация») а затем запускается подписка на событие выполнение «конструктор».
Конструктор запускает работу таймера с параметрами интервал считывания значений тока и напряжения Interval и адрес вызываемого контента Tick по истечению этого интервала. Кроме того, конструктор формирует
значения счетчика усреднения атрибута InitCount на основе параметра Count.
Работа счетчика заключается в запуске подписки Tick по заданному интервалу времени. При каждом выполнении этой подписки атрибуту Counter присваивается значение 0. Машинная команда присваивание формирует адресацию для записи, этого атрибута. А так как на событие Запись атрибута Counter оформлена подписка, то выполняется не присваивание 0, а выполнение подписки. Т.е. вычитание 1 из счетчика. Отметим что начальное значение InitCount этого счетчика Counter присваивается при событии инициализации этого атрибута. Событие инициализации атрибута происходит перед присваиванием первого значения отличного от _ (отсутствие значения) и подписка не это событие выполняется перед выполнением назначения адресации. Т.е. либо перед записью, либо перед подпиской для записи, если она есть.
Следующие операторы в подписке Tick - чтение текущих значений тока Electricity и напряжения Voltage с соответствующих портов. Работа обоих атрибутов похожа. По событию запись, параметр-значение суммируется с текущим значением, которое в начале равно 0. Оба значения при адресации для чтения обнуляются соответствующими подписками на чтение.
Вычисление электроэнергии происходит при событии Count=0. Подписка не это событие вычисляет произведение усредненных значений тока и напряжения, при этом адресуясь к этим атрибутам для чтения, обнуляя их значения подпиской на их чтение. Полученное значение электроэнергии присваивается атрибуту Value для суммирования. Что и является результатом работы.