
Насправді це було давно й неправда 😉 Тобто я, звісно, нічого видатного не створив, але приблизно з 2002 року із задоволенням обмірковував і розробляв базу даних, яка, на мою думку, могла стати основою для ШІ. Нижче наведено щоденник цих пошуків: тут описано і перебіг розробки та експериментів, і просто відірваний від реальності потік свідомості та фантазій на цю тему. На цій хвилі я кілька разів створював і переписував із нуля систему зберігання, а також намагався наповнювати її корисними для себе даними й користуватися нею. Зрештою все це з часом заглохло.
24.08.2002 Початок роботи над проєктом.
Основна одиниця інформації в базі даних — «поняття» — має унікальний ідентифікатор. Може мати або не мати одну чи кілька асоціацій.
Асоціація — посилання на інше поняття; також містить коефіцієнт, або ступінь, асоціації. Найчастіше ця властивість двостороння, але коефіцієнти з обох боків можуть відрізнятися. Коефіцієнт набуває значень від 0 (слабка асоціація) до 1 (сильна асоціація). Крім коефіцієнта, асоціація містить причину/джерело/пояснення асоціації, що також є поняттям — посиланням на поняття.
Суб’єкт асоціації — поняття, яке містить цю асоціацію; об’єкт асоціації — поняття, на яке вона посилається.
БАЗОВІ ПОНЯТТЯ:
Необхідний мінімум для встановлення асоціацій між поняттями.
1 — проста асоціація. Позначає простий зв’язок з об’єктом асоціації.
2 — асоціація узагальнення. Узагальнювальний зв’язок з об’єктом асоціації, який при цьому перебуває ієрархічно вище.
3 — асоціація деталізації. Об’єкт асоціації перебуває ієрархічно нижче.
4 — асоціація тотожності.
5 — асоціація протилежності.
База динамічна: у моменти бездіяльності працює процес, який шукає та встановлює нові асоціації, підсилює/послаблює старі, узагальнює й деталізує поняття, усуває непотрібну надлишковість інформації.
25.08.2002 База даних створюється на основі ІБ — тимчасово, оскільки після накопичення певної кількості інформації швидкодія такої БД сильно знизиться. Для звертання до бази в проєкт додано модуль BD, у якому будуть усі процедури роботи з нею. Взаємодія із «зовнішнім» світом відбувається через модуль IO (InputOut).
Модуль IO оперує такими даними, як «Відчуття». Однакові відчуття пов’язуються з однаковими поняттями. Для понять відчуття використовуємо від’ємну область значень ідентифікатора. Тобто вхідний/вихідний потік становитимуть символи UNICODE зі знаком «-».
Резервуємо для цього діапазон — 1..65536.
Обмін інформацією відбуватиметься через так звані канали введення/виведення.
Кожен канал відповідатиме певному джерелу/приймачу інформації. Наприклад, для клієнта «Адміністратор» щоразу відкриватиметься той самий канал, виділений для спілкування безпосередньо з ним. Так ідентифікуватиметься об’єкт спілкування. Крім того, можна буде одночасно використовувати різні канали для обміну з різними джерелами інформації, не заважаючи одне одному.
Модуль IO матиме безпосередній доступ до БД і зможе заносити туди інформацію незалежно від інших модулів. Для прямої низькорівневої взаємодії з БД створено модуль BD0, який буде інтерфейсом доступу до неї. Це спростить заміну ядра БД з InterBase на будь-яке інше.
Визначив асоціацію в модулі BD0:
I_AS = record // асоціація
ID_AS: integer; // ідентифікатор асоціації (0 — асоціацію ще не створено)
Value: smallint; // величина асоціативного зв’язку
ID_PN: integer; // поняття, якому належить асоціативний зв’язок (суб’єкт асоціації); 0 — поняття ще не створено
ID_PN_DST: integer; // поняття, з яким установлено цей асоціативний зв’язок
ID_PN_AS: integer; // тип/вид асоціації
end;
У явному вигляді створюватимуться лише асоціації. Якщо поняття, якому належить асоціація, не існує, воно створюватиметься автоматично. Ознакою відсутнього поняття буде 0 як його ідентифікатор.
До BD0 додано процедуру BD0_NewAssociations(List: TList), яка додає в БД нові асоціативні зв’язки відповідно до списку. Список складається із записів типу I_AS.
Увесь список додається в одній транзакції. Якщо поняття, якому належить створюваний зв’язок, не існує, створюється нове поняття. Під час одного виклику цієї процедури можна створити не більше одного нового поняття.
Додано процедуру:
function BD0_GetPNAS(ID_PN: integer; var List: TList): integer; // пошук усіх понять, з якими це поняття встановило асоціації
// Повертає список List усіх асоціацій I_AS і кількість елементів у списку на виході
Увів список зарезервованих понять — тих, які мають від’ємний ідентифікатор.
65536 — для символів UNICODE, з них 256 керувальних (насправді поки лише 4 керувальні).
65536 — для каналів введення/виведення. Разом 32768 каналів: вони розташовуються, використовуються та резервуються попарно.
Ще потрібно придумати, як реалізувати відчуття часу; точності до секунди, найімовірніше, буде достатньо. Почав розробляти блок IO й зрозумів, що спершу все-таки доведеться реалізувати відчуття часу. Довго думав: це не так просто…
Довелося додати ще кілька зарезервованих понять: 60 для секунд, 60 для хвилин, 24 для годин, 31 для днів, 12 для місяців, 10 для років, 10 для десятиліть, 10 для століть і 10 для тисячоліть. Діапазон сприйманих значень вийшов від 0 до 9999 року. Ще зарезервовано 2 поняття для високосного й невисокосного року. Інші значення ця штука зрозуміти не зможе.
Додано поняття для асоціації заданого поняття з певним моментом часу, а також зворотної асоціації:
— момент часу. Включає/узагальнює/деталізується на такі поняття:
Наведені нижче поняття використовуються для зв’язування поняття конкретного моменту часу, заданого однією або кількома частинками, з поняттям частинки часу з діапазону FFFD0000..FFFD00EB.
— повне значення року включає/узагальнює/деталізується на:
— значення тисячоліття;
— значення століття;
— значення десятиліття;
— значення року;
— значення типу року ([не]високосний).
— повне значення дня року включає/узагальнює/деталізується на:
— значення місяця;
— значення дня місяця;
— значення дня тижня.
— повне значення часу доби включає/узагальнює/деталізується на:
— значення години;
— значення хвилини;
— значення секунди.
— «цьому значенню часу відповідає» — поняття для зв’язування значення частинки часу (FFFD0000..FFFD00EB) та поняття конкретного моменту часу, заданого однією або кількома частинками.
Разом служба відчуття часу з’їла — зарезервувала собі — 256 понять. Її винесено в модуль DateTime; туди ж додано всі константи понять, необхідні для її роботи.
Список базових понять уже настільки великий, що довелося винести його в окремий файл проєкту Default.txt.
26.08.2002 Передбачається реалізувати механізм приховування роботи пристроїв введення/виведення та служби відчуття часу — HideCall. Він аналізуватиме всі звертання до пам’яті й так звані «думки». Простіше кажучи, для введення/виведення інформації ШІ достатньо лише «подумати» про це: створити поняття приймання/передавання потрібної інформації, установивши асоціацію з відчуттям/поняттям введення/виведення. Інформацію з відкритого каналу модуль IO заноситиме безпосередньо в пам’ять, автоматично встановлюючи необхідні асоціації з джерелом, попередньою порцією інформації з цього каналу та часом її отримання. У цьому сенсі служба часу схожа на «імплант», за допомогою якого програма дізнається поточний час: вона не знає, як це робить, просто знає його, і все…
Так само ми не знаємо, як бачимо: просто бачимо. До речі, цей ШІ доведеться навчати чисел і лічби із самого нуля, зокрема математичних дій — наприклад, рахувати у стовпчик… Ніби якось безглуздо 🙂 Комп’ютер — і раптом рахує повільно та у стовпчик!!!
Але особисто я не бачу іншого способу, хоча два варіанти все-таки є. Ми, наприклад, коли хочемо щось швидко обчислити, беремо калькулятор. ШІ теж можна дати віртуальний калькулятор: зарезервувати для нього один із каналів введення/виведення й навчити користуватися ним. Обчислення все одно підуть помітно швидше. У крайньому разі можна зробити так само, як зі службою часу: створити службу обчислень і під’єднати її до певних «нервів». Якщо виникне потреба щось обчислити, ШІ достатньо буде подумати про це — і він уже знатиме відповідь. Але й без калькуляторів завдання непросте, тож поки ну його: знадобиться — порахує у стовпчик. Загалом це ідеї на майбутнє.
Служба часу вже вміє створювати поняття поточного моменту. У поточній реалізації на одне таке поняття йде 22 асоціації, кожна по 18 байтів у пам’яті: 18 * 22 = 396 байтів на асоціації та 4 на саме поняття, разом 400 байтів. Такими темпами бази в 1 ГБ вистачить лише на 50–55 мільйонів зв’язків, а з урахуванням простору для індексів — на 35–40.
На практиці база з 10000 зв’язків зайняла 1162240 байтів, тобто по 116 байтів на запис. Тоді 1 ГБ вистачить приблизно на 9.000.000 зв’язків. Коротше, InterBase для цього не годиться: потім доведеться писати власний рушій.
Додано модуль оптимізації Optim. Він оптимізуватиме зв’язки й усуватиме їхню надлишковість, помітно зменшить обсяг бази та час пошуку потрібних понять. Виділення й злиття тотожних понять; видалення понять, з якими не встановлено жодної асоціації, — загублених понять. Щонайменше модуль отримуватиме керування в моменти бездіяльності системи; також, імовірно, йому виділятимуться невеликі кванти часу під час додавання нових зв’язків, щоб за можливості одразу їх оптимізувати.
27.08.2002 Логічно було б організувати ще одне базове узагальнювальне поняття — «асоціація», від якого походили б усі інші поняття. Це допоможе чітко визначати можливість установлення асоціативного зв’язку за допомогою заданого поняття.
Ще знадобиться за допомогою набору понять установлювати правила асоціювання об’єктів, щоб ШІ міг самостійно створювати власні види асоціативних зв’язків і використовувати їх.
29.08.2002 Для нормальної роботи служби часу вводимо поняття поточного моменту (FFFD 00 FD), тобто теперішнього часу.
Воно використовується як джерело асоціації на поняття, що містить значення всіх складників поточного часу. Служба часу отримує керування з модуля HideCall для створення поточного поняття часу й асоціації на нього від поняття (FFFD 00 FD). Простіше кажучи, щойно запитується список асоціацій поняття FFFD 00 FD, служба створює поняття поточного моменту зі значеннями всіх складників часу й асоціацію від FFFD 00 FD до цього нового поняття. Попередня асоціація від FFFD 00 FD видаляється; перед цим перевіряється, чи використовує хтось поняття, на яке вона вказує: якщо ні, воно теж видаляється. Залишається лише щойно створена асоціація. Потім керування повертається модулю BD0, який видає запитаний список, включно з новою асоціацією. Далі вже можна створити двосторонню асоціацію із цим поняттям від будь-якого потрібного поняття.
31.08.2002 Організовано систему прихованих викликів. Створено такі виклики:
procedure HC_BeforeNewAs(List: TList) — викликається перед створенням серії нових асоціацій
procedure HC_AfterNewAs(List: TList) — викликається після створення серії нових асоціацій
procedure HC_BeforeGetAs(PN: integer; List: TList) — викликається перед запитом списку асоціацій цього поняття
procedure HC_AfterGetAs(PN: integer; List: TList) — викликається після запиту списку цього поняття
Усередині цих процедур будуть тіла обробників викликів.
Схоже, процес засвоєння інформації дуже нагадуватиме розчинення: отримавши інформаційний блок, ШІ не зберігатиме його в початковому вигляді, а ніби засвоюватиме, розчиняючи у власній пам’яті. Частинки інформації цього блоку набуватимуть зв’язків з уже наявними поняттями й самі перетворюватимуться на поняття. Повне засвоєння може вимагати значних ресурсів, тому його, найімовірніше, розділено на два етапи. На першому, відразу після отримання блоку, встановлюються найочевидніші асоціації з його інформацією. Під час подальшого аналізу глибина асоціацій, за якою сканується пам’ять, зростатиме до певного значення. При знаходженні збігів, закономірностей та інших зв’язків установлюватимуться асоціації з уже наявними й засвоєними поняттями. Після досягнення певної глибини сканування, найімовірніше за допомогою рекурсії, інформацію можна вважати засвоєною. Відтоді вона вже не є окремим блоком і не має чітких меж у пам’яті.
04.09.2002 Додано функції BD0_NA(List: TList) і BD0_GP(ID_PN: integer; var List: TList): integer; це аналоги BD0_GetPNAS та BD0_NewAssociations, але перед їхнім виконанням і після нього керування передається модулю HideCall для обробки базових асоціацій.
Оптимізовано роботу BD0_NewAssociations і виправлено логічну помилку.
Колупатися в базі вручну стало незручно: доведеться зробити зручні засоби налагодження та моніторингу вмісту пам’яті.
Створено процедури читання/зміни приміток ідентифікаторів:
procedure BD0_SetNote(ID: Integer; S: string);
function BD0_GetNote(ID: Integer): string;
07.09.2002 Думаю, потрібно реалізувати єдиний механізм введення/виведення інформації, який дозволить взаємодіяти з будь-якими зовнішніми пристроями. Зі службою відчуття часу я, схоже, трохи поспішив: на початковому етапі вона не потрібна й лише ускладнює справу.
08.09.2002 Вирішив не лише відкинути службу відчуття часу, а й спростити введення/виведення інформації. Воно відбуватиметься через один канал. Попередня схема, де для кожного символу було окреме «відчуття», надлишкова: будь-якої миті на вході може одночасно «відчуватися» лише один символ. Поки кількість відчуттів скорочується до восьми, тобто максимум 256 варіантів відчуттів. З області базових понять також буде вилучено всі поняття служби часу.
09.09.2002 Переглянуто схему базових понять і відчуттів — помітно спрощено:
0000 00XX — діапазон відчуттів введення/виведення.
0000 0100 — пов’язує конкретне поняття з поняттям відчуття.
0000 0101 — поняття введення інформації. При надходженні відчуття m створюється нове поняття n; M асоціюється з N через 0000 0101. Зворотна асоціація створюється через 0000 0100.
Для базових понять зарезервовано область ідентифікаторів до 0000 01 FF включно.
Це абсолютний мінімум, який дасть системі змогу самостійно сприймати й запам’ятовувати інформацію, але ще не аналізувати її. У принципі можливе й виведення інформації, але на цьому етапі воно не має сенсу. У діапазоні відчуттів введення/виведення визначимо відчуття початку/кінця рядка:
0000 0001 — відчуття початку повідомлення.
0000 0002 — відчуття завершення повідомлення.
При введенні символу (відчуття) “O” утворюється нове поняття “New”. Вони пов’язуються двома асоціаціями за схемою:
O – 00000101 – > New
New – 00000100 – > O
Написано procedure IO_Input(Str: string); — приймання рядка символів із зовнішнього пристрою в пам’ять. Процедуру перевірено, вона працює нормально. Але така система не дає змоги встановити послідовність приймання відчуттів, а при прийманні кількох повідомлень — послідовностей відчуттів — неможливо визначити, які символи належать якому повідомленню.
10.09.2002 Додано два види асоціацій:
0000 0102 — указує на наступне прийняте відчуття після цього.
0000 0103 — указує на відчуття, прийняте перед цим.
Скасував символи початку й кінця рядка як надлишкові. Задіяв два вказані види асоціацій. Тепер за окремим символом легко знайти наступний чи попередній або весь ланцюжок символів. Послідовність надходження ланцюжків поки визначити неможливо. Процедуру знову налагоджено.
12.09.2002 Додано асоціації:
0000 0104 — указує на належність цього поняття до поняття екземпляра прийнятого ланцюжка символів.
0000 0105 — указує, що цьому поняттю екземпляра прийнятого ланцюжка символів належить цільове поняття.
Вони дають змогу оперувати поняттями екземпляра прийнятого ланцюжка символів. Уже використовуються при його прийманні.
Відповідно, логічно додати асоціації:
0000 0106 — указує на наступний екземпляр ланцюжка прийнятих символів.
0000 0107 — указує на попередній екземпляр ланцюжка прийнятих символів.
Для їх використання потрібно знати ідентифікатор поняття екземпляра прийнятого ланцюжка. Можна створити поняття останнього прийнятого ланцюжка, яке завжди на нього вказуватиме.
14.09.2002 Оптимізація коду, поліпшення його читабельності, перевірка. При додаванні нових асоціацій списки більше не використовуються, що значно зручніше.
21.09.2002 Розпочато блок графічного представлення пам’яті.
06.10.2002 Думаю трохи змінити систему сприйняття. Поки вона дискретна, але має стати безперервною: замість вхідних ланцюжків символів буде один безперервний потік, який система сама розділятиме на ланцюжки, фрази й слова. Блоку оптимізації теж потрібні істотні зміни: його буде повністю реалізовано в пам’яті пристрою, що дасть гнучкість для зміни й доповнення правил оптимізації. Але, найімовірніше, це пізніше, бо ще не зовсім ясно, як краще організувати механізм сприйняття та впливу блоку оптимізації зсередини.
09.10.2002 Трохи допрацьовано блок графічного представлення. Оскільки система сприймає безперервний потік символів, поняття ланцюжка символів поки не потрібне.
16.10.2002 Вилучено всі поняття «ланцюжків» символів.
Додано нове базове поняття:
0000 0104 — поняття останнього прийнятого відчуття.
Ще як мінімум знадобиться поняття асоціації… На мою думку, йому ніщо не заважає бути і поняттям, і асоціацією, але, найімовірніше, краще дотримуватися прийнятої концепції й окремо ввести асоціацію:
0000 0105 — указує на екземпляр поняття останнього прийнятого відчуття.
Додано процедуру видалення асоціації за номером:
procedure BD0_DelAS(ID_AS: integer);
Поступово почала складатися система умовних позначень для запису алгоритмів:
<поняття-1> — <асоціація> -> <поняття-2> — схематичне позначення асоціативного зв’язку.
CREATE_AS (асоціація) — створити асоціацію
DELETE (асоціація) — видалення асоціації
DST (асоціація) — узяти ідентифікатор приймача асоціації
VARIABLE := — присвоїти змінній значення
xxxxxxxx — значення не визначено
Алгоритм сприйняття нового відчуття:
Вхід: O — прийняте відчуття
New — поточне утворюване поняття
begin
CREATE_AS(O – -00000101 – > New)
CREATE_AS(New – -00000100 – > O)
LAST := DST(00000104 – -00000105 – > xxxxxxxx)
if LAST <> 0 then begin
CREATE_AS(New – -00000102 – > LAST)
CREATE_AS(LAST – -00000103 – > New)
end
DELETE(00000104 – -00000105 – > xxxxxxxx)
CREATE_AS(00000104 – -00000105 – > New)
end
17.10.2002 Перше завдання виконано. Сприйняття працює. На цьому етапі система здатна просто сприймати й запам’ятовувати вхідні відчуття. Щоправда, на основі ІБ це дуже повільно… Тестовий текст на 13000 символів система сприймала близько 20 хвилин. Після цього у БД було близько 13000 понять, 52000 асоціацій, а сама база зайняла 6680000 байтів. Коротше, від мегабайтного фрагмента тексту база виросте на 500 мегабайтів…
23.10.2002 Дописано модуль графічного представлення пам’яті. Картинка виглядає досить моторошно… Гарно, звісно, але практичного інтересу не становить.
25.10.2002 Перший із процесів обробки знаходитиме збіги й логічно пов’язуватиме групи понять, що збігаються, усуваючи надлишковість інформації.
07.11.2002 Розпочато модуль “Constant.pas”, у якому буде оголошено всі константи, а також процедуру ініціалізації бази й створення всіх початкових базових понять та асоціацій.
Додано нове базове поняття:
0000 0106 — базове поняття асоціації, батьківське для всіх інших її видів.
Створюємо і прямий, і зворотний зв’язок. Щодо зворотного ще не зовсім ясно, чи знадобиться він. Але згідно з раніше прийнятою концепцією краще завжди створювати обидва.
Тепер легко буде з’ясувати, чи досліджуваний об’єкт є асоціацією, тобто нащадком цього поняття.
Майже закінчив читати гілку про ШІ на ixbt.com. На жаль, нічого цінного там не знайшов. Два основні напрями — реляційні БД та нейронні мережі.
Найближчим до мого проєкту, схоже, є Vladymir Rybinkin, але в нього трохи інші завдання, хоча основна суть БД значно ближча до моєї реалізації, ніж у решти.
Поки не вдається чітко сформулювати основні принципи обробки інформації в моїй БД, хоча відчуваю, що тупцюю десь поруч 🙂
Процеси обробки, за ідеєю, мають бути лавиноподібними: кожна зміна БД породжуватиме нові процеси обробки інформації. Це має відбуватися автоматично — на підсвідомому, неконтрольованому рівні системи.
10.11.2002 Беремо поточне досліджуване поняття, складаємо список асоційованих із ним об’єктів, перебираємо його. Поки не вдасться оптимізувати, беремо чергове поняття зі списку й складаємо список об’єктів, на які воно вказує, використовуючи ту саму асоціацію, що й для початкового поняття.
Порівнюємо асоціації досліджуваного поняття з асоціаціями знайденого.
Ось тут я й застряг. Легко сказати «порівнюємо»… Ні, не так. Спробуємо з іншого боку. Якщо не вдається знайти модель «універсального» оптимізатора для будь-яких даних, спробуємо створити клас понять, який буде множиною моделей даних, описуючи їхню структуру та способи представлення у вигляді понять. Це й будуть метадані. Вони мають описувати структуру, залежності та всі зв’язки між поняттями. Це ті самі дані, але вже ніби осмислені й зрозумілі, розібрані на складники та об’єднані з відомим «уявленням про світ». Решта — лише потенційні дані: хоч вони й пов’язані з метаданими, їх не усвідомлено/не зрозуміло/не оброблено, вони не мають інформаційної цінності, але потенційно можуть породити метадані. Дані можуть не перейти в метадані з кількох причин:
1. Брак інформації для обробки/розуміння.
2. Залишено для майбутньої глибшої обробки: при надходженні виконується лише поверхнева обробка, а якщо її недостатньо, потрібно збільшувати глибину.
3. Інформація не має сенсу, помилкова або є просто шумом.
Загалом третій пункт — майже те саме, що перший: за браку інформації про структуру дані можуть здаватися помилковими або шумом без змістовного навантаження.
Отже, вся область понять буде поділена на метадані та просто дані.
11.11.2002 Як би мені цього не хотілося, але, на мою думку, жодні універсальні алгоритми обробки не можуть самі створити «правильну» початкову структуру даних, яка згодом зможе самостійно «дрейфувати» в потрібному напрямку.
Процедура ініціалізації БД — CreateBase — надає їй початкову стартову структуру.
Спершу створимо базові асоціації, які й формують множину асоціацій.
// Створюємо і прямий, і зворотний зв’язок
BD0_NA(pn_association, pn_svz, pn_association);
BD0_NA(pn_svz, pn_association, pn_association);
BD0_NA(pn_association, pn_in, pn_association);
BD0_NA(pn_in, pn_association, pn_association);
BD0_NA(pn_association, pn_prev_o, pn_association);
BD0_NA(pn_prev_o, pn_association, pn_association);
BD0_NA(pn_association, pn_next_o, pn_association);
BD0_NA(pn_next_o, pn_association, pn_association);
BD0_NA(pn_association, pn_last_oa, pn_association);
BD0_NA(pn_last_oa, pn_association, pn_association);
Далі потрібно сформувати всі основні множини:
(насправді це поки лише у планах) {
— множина всіх вхідних понять;
— множина всіх друкованих символів;
— множина символів-роздільників;
— множина спеціальних символів;
— множина символів російського алфавіту;
— множина символів англійського алфавіту;
— множина символів-цифр;
— множина символів нижнього регістру;
— множина символів верхнього регістру;
— кожен символ нижнього регістру має бути протиставлений відповідним символам верхнього регістру, що має відповідати наступному рівню абстракції.
Як не крути, потрібні логічні поняття.
Необхідно ввести деякі поняття високого рівня абстракції:
— поняття множини;
— поняття протилежності;
— поняття послідовності — загальне, а не наступного чи попереднього елемента;
— поняття наступного елемента послідовності;
— поняття попереднього елемента послідовності;
— поняття початку послідовності;
— поняття кінця послідовності.
* Рано чи пізно знадобиться вказати послідовність символів російського й англійського алфавітів, зазначивши початок і кінець алфавітної послідовності.
** У ще віддаленішому майбутньому варто розглянути поняття транскрипції — відповідності літер і буквосполучень звукам як російського, так і англійського алфавітів. Надалі такі поняття можуть відкрити дуже цікаві можливості.
Додано процедуру очищення БД BD0_Clear.
Трохи змінено алгоритм установлення примітки: якщо об’єкт не існує, його буде створено.
Об’єкти, через які інформація вводиться в систему, раніше я називав вхідними відчуттями або вхідними поняттями. Ці терміни звучать не зовсім правильно, тому далі називатиму їх інтерфейсними: це точніше відображає їхній зміст, до того ж через них можливе не лише введення, а й виведення інформації.
До ініціалізації додано формування всіх інтерфейсних понять діапазону $00..$FF.
Найбільш незрозуміле й страшне поки — налагодження створюваної структури, пошук логічних помилок і невідповідностей, а тим більше їх виправлення. Просто видаляти поняття не можна: потрібно знаходити всі зв’язки. Будь-які зміни також зачеплять сусідні пов’язані елементи структури, і ще не ясно, як відстежувати та оцінювати ці зміни.
Назріла потреба ввести поняття множини:
pn_set $00000107; // базове поняття множини
pn_set_in $00000108; // указує на належність цього поняття до множини
pn_set_pn $00000109; // указує на поняття, яке входить до цієї множини
Також давно час увести загальні поняття, що вказують на екземпляр поняття (класу) та поняття екземпляра, тобто на його клас:
pn_uin $0000010A; // указує на екземпляр цього поняття
pn_base $0000010B; // указує на базове поняття, екземпляром якого є це поняття
Поняття, створене таким методом, успадковує всі властивості «батька».
Уже скоро й тут заплутаюся: пора прийняти якісь спільні терміни. Мабуть, найкраще взяти термін «клас» для того, що раніше називалося «поняттям», а також термін «екземпляр класу». Це означатиме майже те саме, що й в інших мовах програмування.
Створено множину інтерфейсних понять із прямими й зворотними асоціаціями.
13.11.2002 Викинув до біса блок графічного представлення… Паскалівських файлів у проєкті залишилося трохи більше 13 кілобайтів.
До ініціалізації додано множини символів російського й англійського алфавітів. Трохи нормалізовано решту коду.
Виникла ідея не писати блок ініціалізації на Pascal, а розробити для цього спеціалізовану скриптову мову. Так було б зручніше, адже передбачається закласти чималий обсяг початкової інформації. Потім можна навіть зробити функцію експорту всієї бази у скрипт. А сам скрипт хотілося б навчити не лише створювати поняття та зв’язки, а й змінювати створені раніше.
14.11.2002
Файл ініціалізації матиме приблизно такий формат:
‘:’ — символ-роздільник полів
перше поле завжди вказує на тип рядка
‘#’ — коментар
‘+’ — створюється нове поняття; ідентифікатор можна не зазначати
+ : [00000000: ]pn_name: короткий опис
‘-‘ — створюється асоціація та, за потреби, зворотна асоціація
– : pn1: as1: pn2[: as2]
15.11.2002
Думав, як правильно оперувати множинами.
Поки придумав таке:
1. Предком підмножини завжди має бути множина; асоціація встановлюється через стандартні pn_base, pn_uin. У корені всіх множин — базове поняття множини всіх множин pn_set.
2. Нові поняття включаються до множини через асоціації pn_set_in, pn_set_pn.
3. До підмножини можуть входити лише поняття, які входять до множини її предка.
4. Множина може містити інші множини.
Пункт 3 ще під питанням: можливо, це лише зайве обмеження.
Додано множину десяткових цифр pn_set_dec.
16.11.2002 Схоже, із множинами англійських і російських символів я поспішив. Спершу варто попарно об’єднати символи верхнього й нижнього регістрів.
Обсяг паскалівського коду почав підозріло швидко рости… Думаю, краще було б уносити всі нові поняття безпосередньо в БД, добре документуючи процес, роблячи резервні копії та зберігаючи їх разом із документацією на момент копіювання, щоб можна було повернутися на кілька кроків назад.
Тобто найкраще зайнятися програмуванням безпосередньо БД. Для цього, крім наявного перегляду, потрібно створити утиліту редагування БД, яка працюватиме з мнемонічними кодами раніше визначених понять. Ще краще, якщо вона зможе виконувати щось на зразок скрипта… Хоча друге загалом не обов’язкове: далі однорідність понять, таких як символи алфавітів, начебто має зменшуватися.
Поки скрипт працюватиме за правилами від 14.11.2002 з деякими спрощеннями:
1. Усі визначення констант буде винесено в окремий файл const.txt.
2. Основний скрипт буде в init.txt; формат записів приблизно такий:
pn1: < note > — визначення нового поняття. Якщо його мнемонічного імені немає в константах, створюється нове поняття.
pn1: pn2: as1 — визначення асоціації pn1 – as1 – > pn2
pn1: pn2: as1: as2 — визначення асоціації pn1 – as1 – > pn2 та зворотної асоціації as2
Блок читання констант начебто запрацював. Уже сформовано основні константи інтерфейсних понять із префіксом ifc_, після якого йде сам символ поняття.
Блок створення понять та асоціацій теж начебто працює.
Великі й малі літери російського алфавіту асоційовано між собою: створено множини літер А, які включають велику й малу літеру А, і так далі. Ці множини отримали префікс rus_А, де А — російська літера, включно з твердим і м’яким знаками. Те саме зроблено із символами англійського алфавіту, префікс — eng.
17.11.2002 Створено множину російських літер rus, що містить літери російського алфавіту. Аналогічно створено множину англійських літер.
Поняття pn_next_o, pn_prev_o спрощено до pn_next, pn_prev: вони стали базовими.
Увів поняття first, last — перший і останній елементи. Це вказівники на протилежні елементи послідовності чи множини, тож, можливо, найкраще об’єднати їх в одну множину — протилежність.
З’явилися сумніви щодо правильності базової концепції обраної моделі БД. Треба подумати, чи можна ще спростити її й обійтися одним типом зв’язку на всі випадки. Раніше був певен, що це неможливо, але тепер з’явилися свіжі міркування.
24.11.2002 Так і не вдалося чітко сформулювати основи системи з єдиним типом зв’язків, тож поки працюватиму з тим, що є.
Створено множину протилежних елементів prt, до якої входять first і last.
Нове поняття as_protiv: указує на елемент, протилежний цьому.
Через цю асоціацію пов’язані first і last, а також pn_prev і pn_next.
/Далі — потік утопічних думок у дусі «ніби систему вже написано»/
Загалом система вийшла досить цікавою. Вона складається з багатьох окремих елементів, кожен із яких сам по собі не містить інформації. У системі немає типів даних або даних узагалі: все сумісне з усім. Тому немає проблеми несумісності типів. Немає поняття формату даних — отже, зникає несумісність форматів. Немає адресації збережених знань — отже, немає побоювань, що колись забракне розрядності адреси. Система не потребує індексації: усі дані вже індексовані, не потрібно перебирати безліч елементів, щоб знайти потрібний. Не потрібне й стиснення збережених даних — інформація вже стиснена. А внутрішній віртуальний простір нічим не обмежений. Система повністю абстрагована від будь-яких носіїв інформації. Під час роботи вона не маніпулює великими обсягами даних — це не потрібно. Не можна сказати, що ця інформація зберігається тут, а та — там: вона невіддільна від решти системи.
Система не статична: вона змінюється з отриманням нових даних, сама її структура змінюється під час сприйняття інформації. Отримана інформація змінює систему; вона сама здатна змінити себе. Якщо система «сприйняла» інформацію й змінила свій стан, цей процес практично незворотний, хіба що повністю зберегти систему перед сприйняттям ключової інформації, а потім відновити. Система змінює стан лише після отримання змістовних даних і в межах свого «розуміння» цього змісту. Беззмістовні дані практично не мають змінювати її стан і, можливо, згодом будуть відкинуті/«забуті», хоча навряд чи в цьому виникне потреба. Поки взагалі не ясно, чи потрібен механізм забування, хіба що як запобіжник/захист від дурнів. Та й у поняттях системи беззмістовні дані не займають місця: фізично місце, звісно, займається, але логічний простір від цього не перевантажується.
Ця система «в собі»: вона написана не якоюсь мовою програмування, а власною внутрішньою мовою. У неї немає розрядності чи діапазону, що обмежує систему, а отже, не може бути переповнень, винятків, помилок… Немає чому переповнюватися. У неї немає «рідної» системи числення, як у процесора, тому не важливо, у якій системі числення відбуватиметься «навчання».
Думаю, як визначати послідовність. Із наступним/попереднім елементом усе ясно, але як краще задати початок/кінець? Насамперед уся послідовність має бути певним поняттям: множиною її елементів або хоча б множиною початку й кінця. Тоді від загального поняття можна створити асоціацію first на перший елемент і last — на останній, а від першого — next на наступний, і так далі. Аналогічно від last створюється асоціація prev на попередній.
Визначено послідовність літер російського/англійського алфавіту.
Визначено множину символів-цифр “dec” та їхню логічну послідовність.
26.11.2002 Стандартний підхід до інформаційних систем:
інформація + алгоритм = інформація.
Я вже зрозумів: навіть такий, здавалося б, високий рівень абстракції для моєї системи занизький — це зупинка на півдорозі. Потрібно досягти:
інформація + інформація = інформація.
Це найвищий рівень абстракції, на якому навіть алгоритми вироджуються у просту інформацію, яку вже ніяк не можна назвати алгоритмом. Уся інформація з пасивної стає повністю активною. У новій системі інформація зможе безпосередньо взаємодіяти з іншою інформацією.
Одна з основ системи — поняття множини “pn_set”. Множина завжди означає «будь-який із». Тепер мені знадобилося схоже поняття, але з іншим змістом: не «один з елементів», а «всі елементи». Множина ніби об’єднувала елементи за принципом or, а тепер потрібне поняття, яке об’єднає їх за and. Це дозволить увести «послідовність» або «скінченну послідовність», до яких входитимуть указівники first, last, next, prev. Тоді до «послідовності» можна віднести всі поняття з таким набором указівників. Хоча… Можливо, краще зробити трохи інакше: просто створити множину «послідовність» і внести до неї всі поняття-послідовності. Теоретично система зможе проаналізувати їх, виявити спільні ознаки й самостійно створити узагальнене поняття послідовності. Цей варіант мені подобається більше, але потрібно подумати, як автоматично «верифікувати» й доповнювати ознаки входження поняття до множини, як зберігати набір ознак і організувати правила та винятки для них.
28.11.2002 Додано групу понять:
pn_set_and: 00000114: базове поняття набору понять, які до нього входять
as_set_and_in: 00000115: указує на належність цього поняття до зазначеного набору
as_set_and_pn: 00000116: указує на поняття, яке входить до цього набору.
Хоч усе ніби виходить логічно, мені здається, обраний спосіб асоціацій має надто велику інформаційну надлишковість. Через це важко вирішити, які саме асоціації використовувати для потрібних зв’язків. Тут треба спрощувати; це стало помітно лише тепер. Попрацювавши за поточними правилами, уже бачу, що варто змінити. Наприклад, прийняти таке:
1. Зв’язок завжди двосторонній: з обох кінців є пряме посилання на протилежний елемент.
2. Зв’язок може бути висхідним і низхідним — указівником на батька або предка. Оскільки всі зв’язки двосторонні, указання з одного боку на предка автоматично означає, що зворотний зв’язок указує на батька.
Тоді під час «читання» інформаційної структури основну інформацію несе вже цільовий об’єкт, а не тип зв’язку.
Поки все… Залишилося подумати, чи можна новою моделлю реалізувати все зроблене раніше: чи не надто я спростив, адже інформації може забракнути для існування змістовної інформаційної структури.
Зроблено повну резервну копію системи в архів “init021128_0.rar”.
29.11.2002 Подумав про вчорашню модель. Не підходить: інформації недостатньо. Тож модель поки залишається попередньою.
З’явилися міркування щодо процедури «засвоєння» даних. Універсальної методики не буде. Доведеться написати метаалгоритм, який на основі вже сформованих структур за їхньою «аналогією» оброблятиме решту. Він має виділити спільні ознаки — тип асоціативних зв’язків між уже обробленими даними — і за ними знайти ділянки структури, придатні до перетворення. Спочатку потрібне певне навчання.
Ще думав про програмування методик обробки: програміст спеціальною мовою визначає критерії та спосіб обробки. Це менш гнучко, ніж попередній метод, і нові методи потребують втручання програміста…
Але є надія, що практично придатних методик буде небагато. Нарешті, третій варіант: алгоритму обробки немає взагалі. Погано, але не фатально. Тоді аналіз та обробка нових даних відбуватимуться за допомогою програміста. Це буде просто новий вид програмування, що теж непогано. Все-таки впевнений, що вдасться реалізувати принаймні другий алгоритм. Якщо так, то, накопичивши кілька методик, можливо, побачу закономірності й зможу використати перший варіант.
А поки продовжимо набирати «критичну» масу даних 😉
01.12.2002 Одне з понять, які потрібно якнайшвидше визначити, — «послідовність» і «скінченна послідовність». Якщо між елементами є зв’язки next, prev, то це елементи послідовності. Потрібно також розділяти загальне поняття послідовності й поняття її елемента.
Найімовірніше, правильніше організувати множину послідовностей, а потім, якщо розглядувана множина елементів є послідовністю, включати її до цієї множини.
Залишилося придумати, як зберігати й змінювати критерії. За такого підходу час від часу траплятимуться послідовності, ще не включені до множини послідовностей: система ніби не «усвідомила», що це поняття теж є послідовністю. Таких випадків буде багато. Саме для них потрібні процеси, які безперервно «перетравлюватимуть» інформацію; ідеально тут підійшла б концепція «агентів» — активних складників.
Поки доведеться обмежитися процесами, щоб отримати хоч якогось «тугодума» 🙂 Під час простою цей процес працюватиме на повну потужність: виходить, система ніби спить.
Нове поняття:
serial: 00000117: множина послідовностей
З огляду на ці міркування ліквідовано групу понять, додану 28.11.02: вони не дуже вписуються в архітектуру й виглядають «негарно».
Дивимося, що з визначеного входить до множини послідовностей:
serial: rus: pn_set_pn: pn_set_in
serial: eng: pn_set_pn: pn_set_in
serial: dec: pn_set_pn: pn_set_in
Ще негарно визначено множини, наприклад:
dec: pn_set: pn_base: pn_uin
Це неправильно. Якщо є множина всіх множин pn_set, то для входження решти множин до неї потрібно використовувати “pn_set_pn: pn_set_in”, а не “pn_base: pn_uin”.
Виправляємо…
Отже, “pn_base, pn_uin” поки ніде не використано. Чи потрібне воно взагалі, ще не ясно. Треба подумати: можливо, удасться обійтися й без цього. Все помітно спростилося й поки добре вкладається в систему — а це вже гарна ознака 😉
Із нерозв’язаних питань залишається питання вагових коефіцієнтів: не зрозуміло, чи потрібні вони взагалі.
02.12.2002 Ніяк не визначуся, що робити далі… Потроху визначати слова — цим можна займатися роками. Краще, якби слова вже сприймалися з пристрою введення, але як тоді пов’язувати їх із внутрішніми поняттями? Можна ввести протокол обміну даними з певними керувальними сигналами для прямого впливу. Це також створить додатковий «простір» для подальшого «розвитку». Можливо, варто подумати про «органи» сприйняття зовнішнього статичного сховища інформації.
Отже, протокол обміну все одно доведеться розробляти. Над цим і варто подумати.
08.12.2002 Щоб навчити систему «відчувати» інформацію з вхідного потоку, потрібен вхідний протокол. Тут, схоже, найкраще підходить проєктування згори вниз. Найбільша одиниця потоку — «фрейм»: послідовність символів, обмежена стартовим і стоповим символами. Система має починати аналіз лише після повного отримання фрейму.
ifc_fr_beg: 00000000: символ початку фрейму
ifc_fr_end: 00000001: символ кінця фрейму
Оскільки самі стартові й стопові символи інформації не несуть, їх не потрібно зберігати.
16.12.2002 Довго міркував, чи достатньо прийнятої системи сприйняття для роботи: чи існує певна критична маса системи сприйняття, достатня для подальшого самостійного функціонування й саморозвитку? Висновок — достатньо будь-якої. Система сприйняття не є вузьким місцем чи обмеженням, бо це легко компенсувати оптимально розробленим протоколом обміну. Абсолютний мінімум — канал завширшки один біт.
19.12.2002 На мою думку, обмін із зовнішнім світом на перших етапах узагалі не першочерговий. Поки немає що виводити й вводити, обмін не потрібен: система все одно не зможе обробити нові дані, не зрозуміє їх.
Я вже встиг наплодити достатньо зайвих модулів, тож час переписати все з нуля 🙂 І викинути InterBase. Вважатимемо, що це була версія 0.01. Старими залишаться лише блоки даних const.txt та init.txt, але хочу додати до них включення файлів, об’єднати їх і потім додати вивантаження БД у цьому форматі — на кшталт резервної копії в ІБ.
Почнемо з драйвера БД.
Вимоги до самої бази:
— не має бути прив’язки до розрядності ідентифікаторів;
— за кожним ідентифікатором можуть закріплюватися:
1. Текстова примітка завдовжки 0..255 символів.
2. Необмежена кількість посилань, кожне з яких містить ідентифікатор асоціації та цільовий ідентифікатор.
Оце й усі вимоги до структури даних 🙂 Що простіше, то правильніше. Потрібно реалізувати:
— procedure CreateID(ID: integer);
— procedure SetNote(ID: integer, Note: string);
— function GetNote(ID: integer): string;
— procedure AddAss(ID_SRC, ID_DST, ID_ASS: integer);
— procedure GetAss
Потрібна індексна таблиця ідентифікаторів. Підійде звичайний TList. Ідентифікатор має залишатися незмінним увесь час існування БД, тому ідентифікатори з TList не видалятимуться: лише обнулятимуться або додаватимуться. Створено модуль BD і клас TBD — базу даних.
Додано метод TBD.Init;
Вирішив, що найпростіше зробити через TStringList, де примітки й асоціації зберігатимуться побайтово.
Реалізовано:
procedure Init;
procedure CreateID(ID: integer);
procedure SetNote(ID: integer; s: string);
function GetNote(ID: integer): string;
procedure AddAss(ID_SRC, ID_DST, ID_ASS: integer);
function GetAss(ID, AssNum: integer; var ID_DST, ID_ASS: integer): boolean;
function AssCount(ID: integer): integer;
Власне, все. Основний модуль БД готовий.
Тепер спробуємо переробити старі процедури під новий модуль.
Додано вивантаження бази у файл:
procedure Save(FileName: string);
Створюються два файли: .ai — сама база, .note — примітки.
Переписування процедури виконання скрипта ініціалізації залишу на завтра. Потрібно підтримати включення файлів, поки позбутися блоку констант, повністю перейшовши на імена, та прибрати зайве зі скрипта, залишивши головне: зараз мені зовсім не потрібні всі 256 інтерфейсних понять та дещо пов’язане з ними. Також поки облишу протоколи: з’ясувалося, що передавати/приймати ще нічого. Продовжу «вилизувати» архітектуру, бо від самого початку почав городити монстра — потім не розгребу.
21.12.2002 Викидаємо опис констант “const.txt”. Прибираємо програмне введення базових констант та інтерфейсних понять із constant.pas, а також парсер файлу опису констант.
До основного скрипта додано оператор “$include: filename”, де filename — ім’я файлу, який потрібно включити тут. Спершу думав обмежити область інтерфейсних понять, усунувши різницю між верхнім і нижнім регістрами та, можливо, залишивши лише російські літери. Тепер думаю, що знання людської мови на цьому етапі не потрібне: зайве ускладнення для налагодження. Щоправда, питання, чого тоді взагалі навчати цю істоту, залишається відкритим… 🙂
22.12.2002 Чого навчати, ще не придумав. Але потрібен початковий набір понять для будь-якого завдання — наприклад, поняття множини. Перевірив БД, знайшов пару помилок, перевірив включення файлів, зробив вивантаження у текстовому форматі замість двійкового. Все вивантажується в один файл .base.
За великої кількості елементів виникає багато «довгих» зв’язків, що погіршує стиснення бази й не дозволяє кластеризувати зв’язки зі зменшенням розрядності індексів. Пов’язані поняття за можливості повинні мати близькі індекси. Тоді можна використовувати відносні індекси замість абсолютних: за середньої розрядності 16 біт витрати пам’яті можна зменшити вдвічі та, можливо, підвищити швидкодію.
Поки взагалі прибрав англійські символи з БД — виніс у sym_eng.ai. Додав модуль zvuk.ai із поняттями всіх мовленнєвих звуків.
12.01.2003 До «спілкування» із системою людською мовою все-таки дуже далеко. Введення звуків і слів у БД нічого не дасть без бази понять і змісту: самі слова не тотожні поняттям, а лише є вказівниками, посиланнями та модифікаторами. Без робочої системи понять не буде й контексту. Можливо, піти шляхом модульної бази знань, де кожен модуль міститиме пов’язаний за змістом набір понять і свої зв’язки з іншими модулями, без яких його не можна коректно додати до системи. Грубо кажучи, без модуля знання лічби й цифр система не під’єднає модуль математики. Мене лише мучать невиразні сумніви: зв’язків між модулями може бути так багато, що виключення навіть одного з них стане неможливим через численні посилання з решти. Тоді модульний поділ буде умовним, логічно розділяючи області знань лише для зручності програміста.
14.01.2003 Можливо, спробувати реалізувати модель якоїсь гри, наприклад шахів. Так краще проявляться вузькі місця й недоліки системи. Залишається питання збереження проміжних результатів: чи доведеться використовувати зовнішні сховища тимчасової інформації? Якщо ні, як у термінах системи зберігати поточний стан шахової дошки? І поки погано уявляю практичне використання моделі: чи алгоритм теж буде описаний у термінах системи, як частина загальної моделі, чи стане чимось зовнішнім.
Шахи для початку, мабуть, навіть заскладні — може, хрестики-нулики… Узагалі треба розробити теорію побудови моделей. У хрестиках-нуликах простіше: клітинок небагато, кожну можна описати окремо. Хоча не варто ділити їх на праві/ліві, верхні/нижні: не важливо, яким боком повернута дошка. Модель має враховувати її поворот під час гри. Можна виділити особливі клітинки: центральну, кутові й середні. Але, може, знову зайшов не з того боку… Головне — правила гри, на них треба засновувати модель. Дошка може змінити розміри, стати 4 * 4 чи взагалі безрозмірною. Потрібні поняття сторони, що грає (супротивник-1/супротивник-2), клітинки — порожньої/зайнятої, фігури гравця, фігур у лінію, лінії клітинок.
20.02.2003 Багато міркував, як навчити систему хоча б основ програмування, і з’явилися думки. Немає сенсу починати з формалізованих конкретних понять певної мови. Починати потрібно із самого поняття алгоритму. Щонайменше доведеться ввести «крок алгоритму», хоча це радше точка, у якій виконавець перебуває зараз. Із кожною точкою алгоритму асоціюється поняття, яке й становить крок, задаючи його вектор і кінцеву точку. Алгоритм може містити будь-яку кількість точок, пов’язаних векторами переходів — кроками алгоритму. Крім вектора, вони містять поняття внутрішнього змісту кроку: умову або дію. Зі змістом, який для стислості називатиму кроком, потрібно розібратися детальніше. Не хочеться розділяти його на дію та умову: правильною основною категорією, найімовірніше, є щось одне — і, на мою думку, саме умова.
Дія — лише наслідок/результат. Очевидно, алгоритми мають оперувати поняттями, але ще не бачу ясно, чи потрібно представляти їх змінними.
Тоді вхідні дані можна представити як In_1..In_n, а вихідні — Out_1..Out_n. Але система оперує поняттями, тому на вході буде поняття in, на виході — out. У цьому поки невиразно бачу основу механізму обробки/перетравлення всієї інформації у БД. Це дозволить увести систему логічних висновків, здатну, наприклад, робити таке:
Маємо логічний висновок/правило, яке за вхідним поняттям визначає, чи є воно послідовністю.
Працює приблизно так. На вхід надходить поняття-аргумент; серед його предків рекурсивно шукається належність до множини «перший елемент послідовності». Якщо її немає, поняття не є скінченною послідовністю чогось. Якщо є, шукається належність до останнього елемента послідовності. Якщо знайдено — це скінченна послідовність; якщо ні — відповідно, не є. Тут бачу серйозну заковику: ще не зрозуміло, чи треба обмежувати глибину рекурсивного спуску при переборі предків і як саме. Мабуть, спершу потрібно знайти ситуацію, де це необхідно. Але рекурсивний спуск усе одно доведеться реалізувати — з обмеженням чи без. Найімовірніше, обмеження потрібне. Навряд чи воно буде кількісним: правильніше обмежити глибину якісним переходом. Треба придумати приклад…
Щодо застосування механізму: спочатку уявляв його для створення зв’язків нових понять. Але, можливо, зручніше через нього ввести обчислювані зв’язки. Це зменшить кількість одночасно наявних зв’язків і розв’яже проблему їх перерахунку при зміні функції обчислення.
24.02.2003 Рекурсивний пошук за зв’язками в глибину все-таки доведеться реалізувати. Розпочато процедуру FindRec, але ще не дописано. Треба додати обмеження глибини та виключити пошук уже пройдених вузлів. Останнє, мабуть, реалізую списком пройдених вузлів.
06.03.2003 Прогрес переважно зупинився на виборі відповідного завдання. Основні критерії:
1 — цікавість;
2 — невелика кількість понять.
07.03.2003 Викинув із модуля бази весь зайвий текст і заглушки, залишивши лише робочий код.
10.03.2003 Можливо, спробую на основі семантичних мереж створити віртуальну файлову систему, яка доповнить традиційні FAT і NTFS, але краще підходитиме для пошуку й систематизації — надбудову вищого рівня. Перший етап — перетворення властивостей і зв’язків поточної файлової системи. Після цього фізична структура файлів і папок буде представлена графом, у вузлах якого — файли, пов’язані асоціативними відносинами. Для практичного використання потрібне й символьне представлення файлів та папок, але поки залишу це й обмежуся списком символьних ярликів, кожен із яких відповідатиме своєму вузлу семантичного графа. Нічого нового для цього створювати не доведеться: вже є система «приміток», яка чудово підходить на роль «ярликів».
Написано процедуру, яка рекурсивно сканує каталоги й генерує скрипт, що потім можна передати компілятору разом з іншими.
Виправлено помилку в GetAss.
Згенерував скрипт зі структури диска D — майже 24000 вузлів/понять. Компілятору вже потрібен індикатор прогресу 🙂
Індикатор не допомагає: після обробки 10000 рядків компіляція сповільнюється до 50 рядків за секунду. Основний час, імовірно, іде на пошук потрібного ідентифікатора. Доведеться прискорювати.
Застосував хешування: компіляція прискорилася приблизно в 17 разів. Поки хеш — проста сума кодів усіх символів рядка. Максимальний розмір таблиці — 4096 хеш-значень.
Перевірив швидкість TStringList із великою кількістю рядків. Їхня кількість слабо впливає на швидкість; головне, щоб усе вміщалося в пам’яті 🙂
Один зі способів зменшення обсягів — віртуальні зв’язки. Наприклад, є 50 вузлів із тією самою властивістю. Якщо вони мають спільного предка, властивість можна присвоїти лише йому, а в нащадків вона буде віртуально. Можливо, краще приховати цей механізм, щоб виглядало, ніби нащадки справді мають властивість. Нова асоціація вузла поширюватиметься на всіх нащадків за лініями master/detail у бік detail. Усі асоціації групи master чи detail відповідно мають належати до цієї групи — входити до множини.
Неприємна новина: збережений у файл TStringList назад не завантажується, чого й варто було очікувати. Доведеться робити власне вивантаження/завантаження. Вивантажувати в текстовий формат, мабуть, немає особливого сенсу, крім випадку генерації скрипта.
31.03.2003 Міркування на форумі про розмірність простору шахової дошки змусили мене задуматися. Як визначити розмірність? Чи є універсальні критерії? Чому навіть таку обмежену площину найприродніше представляти двома координатами? Можна обійтися й однією, просто пронумерувавши клітинки. Для спрощення уявімо, що наш простір дискретний: можемо розбити світ на клітинки, як дошку. Чи зможемо тоді використати одну координату замість трьох? Очевидно, ні: простір нескінченний у будь-який бік. Шахова дошка — окремий випадок, але тут усе так само. Припустімо, числа можуть бути від 0 до 7 — три двійкові розряди. Тоді для представлення потрібні два числа. Якщо взяти однобітове двійкове число, знадобиться шість таких чисел: шестивимірний простір — максимум. Отже, розмірність прямо залежить від того, чим вимірювати; нескінченний простір не виміряти скінченними одиницями.
Тепер розгляньмо саму одиницю вимірювання… Як, ви не помітили? Це ж теж простір! Такий самий, нічим не відмінний від інших. Наприклад, будь-яка числова змінна — звичайний одновимірний простір, нескінченний в обидва боки. Він може бути й скінченним: змінна завдовжки N бітів. Визначаючи розмірність, ми лише порівнюємо одні простори з іншими. Найзручнішими виявилися нескінченні одновимірні простори, звідси й визначення нашого тривимірного через три одновимірні. Це зручність, і не більше.
Висновок: будь-який простір вимірюється за допомогою іншого простору.
Разом: мінімальний доступний нам одновимірний простір — 1 біт; максимальний — ціле від нуля до нескінченності (дійсні числа — уже двовимірний простір). Отже, представлення шахової дошки — лише питання зручності, а кількість вимірів лежить у діапазоні 1..6.
Тепер уявіть нескінченну площину, розбиту на квадрати. Яка її мінімальна розмірність? 2? Думаю, більшість так і подумала 🙂 Я теж ще пів години тому… Але ні: виявляється, і цю площину можна представити як одновимірний нескінченний простір.
28.04.2003 Ідея дискретних просторів нічого цікавого не дає 🙂 Шкода.
Знову переробляю головний модуль. Вирішив зробити терміни БД максимально чіткими: перестати оперувати нечіткими «поняттями» й «асоціаціями». Натомість вводяться конкретніші вузол/точка/Point та зв’язок/Link. Так буде зрозуміліше й мені.
Основна робота перейшла на проєкт FBD, куди переїхав головний модуль БД.
З основного модуля прибрано процедуру Save.
03.05.2003 Низькорівневий модуль роботи з БД — UBD; далі йде модуль першого рівня UBD1.
Поряд із примітками в основному модулі додано зберігання імен.
Імена зберігаються в одному рядку з приміткою, відокремлені символом ‘:’.
Спочатку йде ім’я, після ‘:’ — примітка. За замовчуванням присвоюється ‘-‘;
Ім’я або примітка можуть бути порожніми, але не мають містити ‘:’.
Переписано збереження й завантаження. Вивантажується у два текстові файли: FileName.note — імена та примітки, FileName.point — дані про зв’язки.
05.05.2003 Пишеться візуальний редактор. Схоже, зручніше мати багато локальних карт замість однієї глобальної для всього графа — аж до окремої карти для кожного поняття. Карта зберігається в PointMap. Рядок на вузол. Формат: номер вузла, x, y, width, height — через кому. Кількість описаних у рядку вузлів відповідає кількості вузлів у локальній карті заданого вузла.
06.05.2003 Зробив завантаження даних із файлу. Виніс візуальну роботу в PointForm; надалі можна буде відкривати кілька форм для різних операцій.
08.05.2003 Робота над редактором.
09.05.2003 Робота над редактором.
Намагаюся максимально візуалізувати засоби редагування. Створено панель роботи зі зв’язками вузлів, додано прапорець автоматичного реверсу зв’язків. Майже всі зв’язки будуть двонапрямними; за ввімкненого автореверсу підбиратиметься зворотний тип, найімовірніше через ToInverse.
12.05.2003 Усунуто страшну помилку завантаження з файлу.
Почав створювати пробну модель файлової системи.
21.05.2003 Роботу з файловою системою треба організувати трохи інакше, ніж я уявляв. Краще абстрагуватися від дрібниць і відійти від зазначення файлу через шлях. Для цього буде спеціальний модуль, який шукатиме файл за ID і навпаки, а також стежитиме за оновленням, переміщенням, перейменуванням чи видаленням.
Буде створено модуль Files. Поки для ідентифікації файлу обмежуся шляхом, ім’ям і розміром. Потім, можливо, додам контрольну суму.
08.06.2003 Створення нового «компілятора» (unit Compiler).
Формат рядка: роздільник підрядків ‘:’.
include: FileName — включити файл
p: PointName: PointNote — нова точка
l: PointFrom: PointTo: LinkType[:InverseLinkType] — створення зв’язку
До модуля bd додано процедуру clear.
22.06.2003 Знадобилися функції роботи з множинами вузлів. Множини мають передаватися як параметри й результати. Два основні варіанти: стандартно передавати масиви або організовувати множини засобами БД й передавати лише ідентифікатор. Другий однозначно повільніший, але потенційно цікавий: можна передавати ієрархічно організовані дані. Поки спробую його — здається гнучкішим. Усі процедури складатиму в unit ProcDM.
23.06.2003 Спроба створити інтерпретатор, який послідовно виконує команди й використовує стек для параметрів.
Потрібен компонент побудови таблиць. Вхідні дані — Point на таблицю, яка через detail містить рядки, а рядки через detail містять стовпці. Таблиця — ієрархічний список другого рівня.
24.06.2003 Граблі виявилися там, де не очікував. Будь-яка асоціація класу master чи detail має безпосередньо бути detail відповідної множини, інакше не можу визначити її належність. А може, ще простіше: асоціації master і detail не можуть мати нащадків.
30.06.2003 До процедури побудови таблиці як параметр також має передаватися посилання на метадані. Тут вони матимуть вигляд:
p: CDDisk: множина дисків
l: MyCD: CDDisk: detail: master
p: CDName: назва CD
l: CDDisk: CDName: detail: master
p: CDComment: коментар до CD
l: CDDisk: CDComment: detail: master
p: CDNumber: номер CD
l: CDDisk: CDNumber: detail: master
Де CDDisk — множина елементів-рядків таблиці; CDName, CDComment, CDNumber — елементи її рядка.
Відображення таблиці начебто запрацювало, але не подобається громіздкий запис процедури проєкції даних у таблицю.
16.07.2003 Побудовою допоміжних таблиць розв’язано проблему визначення належності точки до master або detail.
Proc.CreateMasterList — будує допоміжну таблицю.
Функції:
function IsMasterLink(Point: integer): boolean;
function IsDetailLink(Point: integer): boolean; — перевіряє належність.
18.07.2003 Метод інтерпретації:
Якщо Point є detail від ExecutionPoints, відбувається виконання; інакше Point кладеться на стек.
Приклад:
StartPoint ForAllLinks detail ifEqual
^стартова точка
^розділення потоків за кількістю зв’язків
Запис — ланцюжок указівників: кожна ланка посилається на наступну, а тип посилання вказує на дію (нащадок ExecutionPoints) або параметр (нащадок ToStack). При аналізі чергової позиції спершу обробляються зв’язки ToStack, потім усі ExecutionPoints. Порядок перебору всередині цих двох груп нічим не визначається.
Форма запису:
:ProcPoint n1 n2: Test000Point n3 n4 end
Токен із двокрапкою попереду — ім’я вузла. Він може стояти будь-де, щоб називати вузли й дозволяти посилання на них.
19.07.2003 unit itpDM містить клас Titp — інтерпретатор із точкою входу Titp.Run(StartPoint: integer).
Усі ExecutionPoints поділяються на:
— базові, вбудовані процедури;
— готові процедури;
— методи розгалуження виконання.
Модуль виконання послідовно проходить усі точки графа.
24.07.2003 Основний цикл модуля виконання написано. Хотів почати з основ — реалізувати процедури, але за поточного способу реалізації допрацьовувати основний цикл не потрібно. Це, безумовно, гарна ознака.
27.07.2003 Підхід із явним задаванням указівників у тілі процедури не підходить: втрачається реентерабельність коду. Це годиться хіба що для констант.
01.09.2003 Поки стек даних.
03.09.2003 Ідея повністю виключити цикли й алгоритми перебору з інтерпретованого коду. «Багатопотоковість»: для кожного знайденого зв’язку інтерпретатор запускає новий процес, передає йому свій повний стан, а після обробки зв’язку той завершується. Так основний процес автоматично розщеплюється на багато паралельних і забезпечується автоматичне завершення. Ще варіант: якщо процедура потребує одного зв’язку як вхідного параметра, а їх більше одного, теж розщеплювати процес, передаючи кожному екземпляру свою порцію даних.
05.09.2003 Процеси мають запускатися подіями, параметр — Sender.
20.09.2003 Дерева з кількома коренями.
Основні відносини між вузлами — master-detail. Вони завжди існують у парі, тож їх має об’єднувати третій, узагальнювальний вузол, чиїми detail вони будуть. Оскільки самі відносини теж вузли, називатиму їх master-point та detail-point, а узагальнювальний — md-point.
Створення інтерфейсу до дерев із кількома коренями. Для візуальної побудови такого дерева потрібно вказати md-point.
Потрібні операції над множинами: перетин, віднімання, сумування.
Додано процедуру видалення зв’язку:
procedure TBD.DelLink(Point, LinkNum: integer);
21.09.2003 Процедура видалення вузла та всіх посилань на нього — не знаходить і не видаляє посилання типу LinkType:
procedure TBD.DelPoint(Point: integer);
Це потрібно для tmp-вузлів, проміжних обчислень.
procedure TProc.CreateLinkMD(MPoint, DPoint: integer);
05.10.2003 Починаємо знову 😉 Дерева з кількома коренями.
— Деревом вважається набір точок, поєднаних відносинами master,detail, з однією кореневою точкою. У цьому тексті вона позначатиметься “LRoot” (LockalRoot).
— Кожне дерево використовує власний набір зв’язків для внутрішньої ієрархії.
Ці два види зв’язків указуються з точки LRoot через указівники LMaster та LDetail; зворотні — відповідно LMaster_, LDetail_.
Позначатиму ці види зв’язків як LRoot_d та LRoot_m. LRoot_d має бути detail від detail, а LRoot_m — detail від master.
— Точка може одночасно бути вершиною лише одного дерева.
Створено графічне відображення дерева.
08.10.2003 Спроба реалізувати перехід між деревами. Щоб скласти список усіх дерев, до яких входить Point, перебираємо всі зв’язки. Якщо знайдено LMaster, цей вузол є коренем дерева, тому додаємо його до списку.
05.11.2003 Насамперед потрібно приділити увагу методиці отримання таблиць із БД.
06.11.2003 Реалізовано щось на зразок стекового буфера обміну (M+) для запам’ятовування поточного Point.
Реалізовано додавання вершини з буфера обміну.
+ Процедура перетворення вибраної точки на вершину дерева.
Дрібне впорядкування коду.
Додавання невеликих, але часто використовуваних процедур пошуку зв’язків, які роблять код наочнішим:
function FindLinkType(Point, LType: integer): integer;
function FindLinkTo(Point, LTo: integer): integer;
function FindLinkTT(Point, LTo, LType: integer): integer;
Виділення в ProcDM процедур підтримки технології «мультидерев»:
procedure PrepareConstants;
procedure ConvertPointToLRoot(P: integer);
procedure DelPointFromTree(Point, Tree: integer);
procedure FindTreeMD(RootPoint: integer; var RootM, RootD: integer);
09.11.2003 Побудова таблиць.
Таблиця задається однією вершиною — називатимемо її коренем таблиці (Table). Дерево Table містить вершини, які є вимірами таблиці. Для одновимірної таблиці, тобто списку, воно міститиме одну вершину; для двовимірної — дві, і так далі. Де у двовимірної таблиці вертикальний вимір, а де горизонтальний, принципово не важливо. Кожне дерево виміру містить вершини — рядки (стовпці) таблиці; кожен рядок, своєю чергою, містить набір вершин — значень комірок. При побудові послідовно перебираються рядки та стовпці, а знайдена точка перетину множин вважається значенням комірки.
14.11.2003 Для отримання таблиць потрібен алгоритм пошуку перетину дерев.
Пошук перетину двох дерев:
function TProc.CrossTree(Tree1, Tree2: integer): integer;
Для зручної роботи створено клас TTree.
Root указує на корінь дерева, задається вручну.
Count — кількість вузлів, включно з кореневим.
M,D — типи зв’язків цього дерева (to_master,to_detail).
При зазначенні Root відразу формується список вузлів дерева, до будь-якої точки якого можна звертатися приблизно так: Tree[N].
Написано тестову процедуру побудови таблиці у HTML.
Поки не ясно, як організовувати послідовності. Логічно зробити їх деревом, де наступний елемент — гілка попереднього. Але чи буде це зручно, особливо для великих послідовностей, ще незрозуміло.
15.11.2003 Послідовності можна реалізувати окремим елементом, як дерево. Але будь-яке дерево, як і послідовність, зрештою є множиною. Отже, послідовність можна представити деревом, вузли якого, крім master-detail, пов’язані ще й next-prev. Від кореня йтимуть first і last; зворотні — first_,last_. У найпростішому випадку дерево однорівневе, всі елементи утворюють один ланцюжок. Але ще не зрозуміло, чи може вузол посилатися на кілька next або prev і чи потрібно це.
Поки схиляюся до «одне дерево — одна послідовність».
16.11.2003 Якщо об’єкту TTree як корінь задано точку, яка насправді не є кореневою вершиною, Count=0. Допрацьовую редактор: дрібні можливості на зразок гарячих клавіш і зручнішої навігації.
17.11.2003 Для простоти й наочності варто створити об’єкт TPoint.
У навігаторі зробити кнопку «назад».
Додати журнал, який можна використати для відкату.
Схоже, допоміжним вузлам, невидимим користувачу, не потрібні імена на кшталт 354_m: це лише зайві витрати пам’яті…
За клавішею F7 до кореня гілки додається нове значення й одразу відкривається режим редагування.
