Эмуляция механизма данных

Перейти вниз

Эмуляция механизма данных

Сообщение автор Gudleifr в Вс Окт 29, 2017 11:19 am

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

FORTH изначально не имеет механизма данных. Он умеет рассуждать только по обратной цепочке. Каждое определение слова гласит: для получения результата надо последовательно выполнить вот эти слова (т.е. сначала получить все промежуточные результаты).
Прохождение прямой цепочки FORTH реализует "применением" программиста (и/или пользователя). Программист просматривает все доступные ему слова и пытается писать определения новых - приближающих его к цели.
Отсутствующий же механизм данных компенсируется простотой написания слов, "ведущих себя как структуры данных" ( CREATE ). Плюсом этого является возможность вместе с данными хранить и некоторое действие его/их обработки ( DOES> ).
***

Такая специфика "действий-данных" приводит к тому, что, формально, запись значений FORTH-переменных есть компиляция! Т.е. изменение кода программы (даже если этот код в особо косвенных FORTH-системах сам рассматривается машиной как данные). То, что мы перекомпилируем только специально выделенные фрагменты (которые привыкли называть переменными, массивам и т.п.), большой роли не играет.
Кстати, именно эта концепция объясняет, почему строгая типизация в FORTH выглядит дуростью. Информацию о типе переменной просто негде хранить (т.к. никаких таблиц переменных периода компиляции не создается, в силу отсутствия в нормальных FORTH-системах понятия "периода компиляции"). Вешать на каждое значение в памяти тег типа? Можно, конечно, и иногда так и приходится делать, как в LISP мы были обязаны обеспечивать выполнимость предиката ATOM. Тэгом может, например, служить адрес кода, прицепленного к CREATE по DOES> (как слово ПЕР? в книге Таунсенда и Фохта - ТЕМА #73, АБЗАЦ #807). Но, на каждую ячейку памяти тег не повесишь.
***

Рассмотрим, как я назвал это раньше, ХАКИНГ-КОДА поподробнее. Как одно слово может изменить другое? Для определенности примем, что оба слова расположены в СЛОВАРЕ (случаи, когда одно или оба из них считываются из ПОТОКА, либо тривиальны, либо сводятся к рассматриваемому).
Более того, буду считать, что область правки ограничивается одним полем одного слова (причем, слова уже найденного в СЛОВАРЕ).
И, очевидно, мы, с одной стороны, можем искать нужный фрагмент по адресу или по сигнатуре, и, с другой стороны, сам алгоритм поиска может находится в перекомпилирующем или перекомпилируемом слове.
***

Простейшим способом будет обычный массив. Слово, в него пишущее, знает начальный адрес, номер элемента и размер элементов. Т.е. это случай внешнего поиска по адресу.
***

Если массив "умный", то применяется внутренний поиск по адресу. Т.е. процедуру поиска записываем в DOES>-часть созданного по CREATE массива. Но, обычно, слово-массив делается векторным, т.е. имеющим несколько точек входа, содержащих правильные процедуры доступа. Одного действия доступа катастрофически не хватает.
***

Случай поиска по сигнатуре обычно связан с представлением данных в виде связного списка. Список перебирается, пока не будет найден нужный "симол", значение которого нужно исправить. Реализация списка на основе массива тривиальна и выбор внешнего/внутреннего поиска - дело вкуса.
***

Слабым местом является большое количество слов, от которых требуется знать подробности реализации структуры данных. Ведь, даже для внутреннего поиска необходимо знание точек входа умного массива. Вместо одного массива я вынужден помнить все способы работы с ним (не даром же инкапсуляция их всех внутри описания массива, предлагаемая объектно-ориентированным программированием, так привлекает любителей "рассуждать прямо").
Возможное решение - сделать данные настолько FORTH-естественными, чтобы прямой доступ к ним не требовался. По крайней мере, указывать имя массива, созданного CREATE, нигде бы не требовалось. Этакая "инкапсуляция наоборот" - не методы спрятаны к объект, а ссылки на объект упакованы в методы.
Еще лучше, если удастся свести данные к FORTH-стандарам и подменить ими обычные ПОТОК, СТЕК, СЛОВАРЬ, ЗНАЧЕНИЕ, БЛОК. К БЛОКАМ, например, можно свести разнообразные базы данных, к ЗНАЧЕНИЮ - результаты любого парсирования, к СЛОВАРЮ - хранение текстовых строк, к СТЕКУ - структуры рекурсивного поиска, к ПОТОКУ - файлы...
***

В этой теме я впадаю в обычный фортерский грех: мол, смотрите, как это сделал. Поэтому воспринимайте ее просто, как пример того, что FORTH можно использовать не только для писания "почти в плохом C-стиле".


Последний раз редактировалось: Gudleifr (Ср Янв 24, 2018 11:05 am), всего редактировалось 1 раз(а)
avatar
Gudleifr
Admin

Сообщения : 879
Дата регистрации : 2017-03-29

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: Эмуляция механизма данных

Сообщение автор Gudleifr в Вс Окт 29, 2017 11:20 am

СВЕДЕНИЕ ПРЯМОЙ ЦЕПОЧКИ РАССУЖДЕНИЙ К МАКРОПОДСТАНОВКЕ

Прямая цепочка рассуждений в простейшем случае выглядит так: вычисляем - запоминаем - используем. СЛОВА-ВЫЧИСЛИТЕЛИ - ПЕРЕМЕННАЯ - СЛОВО-ПОЛЬЗОВАТЕЛЬ. Разумеется, СЛОВА-ВЫЧИСЛИТЕЛИ выполняются в произвольном порядке, заполняя ПЕРЕМЕННЫЕ значениями, а когда-то потом эти переменные использует СЛОВО-ПОЛЬЗОВАТЕЛЬ.
Минус такого подхода - избыточность ПЕРЕМЕННЫХ. Во-первых, почему СЛОВА-ВЫЧИСЛИТЕЛИ не могут обращаться к СЛОВУ-ПОЛЬЗОВАТЕЛЮ напрямую? Во-вторых, программист должен постоянно заботиться о точном соответствии всех трех блоков. Сделал изменения в одном - исправляй оба оставшихся.
***

Решением может быть обычная макроподстановка: в СЛОВЕ-ПОЛЬЗОВАТЕЛЕ оставляются "дыры", закрытые "заглушками", помеченными сигнатурами "символов". СЛОВО-ВЫЧИСЛИТЕЛЬ, определив значение очередного "символа" затыкает им "дыры". Слово, образующее дыру выглядит очень просто:

: LIT0 R> CELL+ >R 0 ;

Т.е. почти обычное слово LIT , используемое для прошивки литералов. Только помещает на стек не следующее за ним значение ("символ"), а всегда 0.
Слово-перекомпилятор получив на вход пару "символ,значение" ищет все подходящие дыры и затыкает их:

: ДЫРА? ( A--F) @ ['] LIT0 = ;
: ЗАПЛАТА? ( W,A--F) CELL+ @ EXECUTE = ;
: ЗАПЛАТА! ( W,A--) ['] LIT OVER ! CELL+ ! ;
: ПЕРЕКОМПИЛИРОВАТЬ ( символ,значение--)
СЛОВО-ИСПОЛНИТЕЛЬ LCOUNT OVER + SWAP DO
I ДЫРА? IF OVER I ЗАПЛАТА? IF DUP I ЗАПЛАТА! THEN
2 CELLS ELSE CELL THEN +LOOP 2DROP ;

Оно заменяет все подходящие пары "LIT0,символ" на "LIT,значение".
СЛОВО-ИСПОЛНИТЕЛЬ выглядит, конечно, страшненько, например, так (в данном случае, оно называется КАРТА. , т.к. рисует карты для военно-морской карточной игры):

CREATE КАРТА. 0 , ]
НЕ-ЧИСЛО LIT0 USSR LIT0 US LIT0 UK LIT0 FRANCE LIT0 JAPAN СИМВОЛ?..
НЕ-ЧИСЛО LIT0 СV LIT0 CVN LIT0 CVH LIT0 CVHG LIT0 BB LIT0 CHG
LIT0 CA LIT0 CG LIT0 CGN LIT0 LPH LIT0 SS LIT0 SSN LIT0 SSGN
LIT0 DD LIT0 DDG LIT0 FF LIT0 FFG СИМВОЛ?..

...

;
HERE КАРТА. - CELL - КАРТА.
CREATE КАРТА.БУФЕР КАРТА. @ ALLOT
КАРТА. LCOUNT КАРТА.БУФЕР SWAP CMOVE

Последние строчки нужны для сохранения чистого недокомпилированного СЛОВА-ИСПОЛНИТЕЛЯ для возможности неоднократной перекомпиляции. Это первый недостаток.
Второй недостаток - видно, что слово получается слишком длинным и несуразным, требующим каких-то сложных слов анализа "что там накомпилировали" (слово СИМВОЛ?.. перебирает стек в поисках найденных символов и отображает их, пока не упрется в НЕ-ЧИСЛО).
Тут вылезает третий недостаток - нужно еще перед вызовом ПЕРЕКОМПИЛИРОВАТЬ точно знать, что нужно вкомпилировать - сам "символ" как константу или "его значение". Слову ПЕРЕКОМПИЛИРОВАТЬ на это плевать, ему - что "символ,значение", что "символ,символ", а, вот, слова-пользователи, вроде СИМВОЛ?.. , еще должны голову поломать. (Я в списке, откуда дергал "символы", использовал "значение" НЕ-ЧИСЛО в качестве указания, что нужно вкомпилировать сам "символ").
Четвертый недостаток - невзирая на то, что все логические переменные уже вычислены при перекомпиляции, сами логические выражения вычисляются словами-пользователями.
Пятый - добавление в структуру каждого нового факта требует полного просмотра структуры на предмет соответствующих "дыр".
***

Однако, несмотря на все недостатки, это чудовище летает. Программист может спокойно переписывать СЛОВО-ИСПОЛНИТЕЛЬ, не правя при этом судорожно списки переменных и не особо задумываясь о том, кто и когда их проинициализирует. А также, затыкать "дыры" можно в любое время и в любом порядке, например, выдергивая наугад "символы" из неупорядоченного множества.
avatar
Gudleifr
Admin

Сообщения : 879
Дата регистрации : 2017-03-29

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: Эмуляция механизма данных

Сообщение автор Gudleifr в Вс Окт 29, 2017 11:22 am

НЕ ТОЛЬКО ПЕРЕКОМПИЛЯЦИЯ, НО И ПЕРЕВЫПОЛНЕНИЕ

Есть такая удобная штука, как Электронные Таблицы (ЭЛТ) - этакий калькулятор с несчетным количеством окошек. И главное свойство ЭЛТ, это отнюдь не способность показывать числа в столбик, а динамический пересчет всех зависимых ячеек при изменении определяющей ячейки. Например, есть ячейка "сумма покупки", а есть много ячеек "список купленного" и, очевидно, "сумма" должна пересчитываться сразу, как в "списке" что-то поменялось. Как такое сделать?
Каждая переменная-ячейка должна иметь список всех ячеек, которые нужно пересчитать (рекурсивно) при ее изменении. И, соответственно, два действия: установка значения/пересчет и передача значения в другую ячейку. Значит, связный список векторных слов. Т.е. специфического вида СЛОВАРЬ. "СЛОВАРЬ наоборот" - связанный в порядке зависимости, а, следовательно, от ранних слов к поздним.
Для простоты будем перерассчитывать не только зависящие формулы, но и все те, что потенциально могут зависеть (т.е. добавлены в СЛОВАРЬ позднее).
На каждом ЭТАЖЕ живет одна формула и несколько переменных. Это те, кто не входят в более ранние формулы, но могут войти в следующую.

VARIABLE ЭТАЖ
: ГОРИЗОНТАЛЬНО ( A1,A2--)
?DUP IF 2DUP @ RECURSE ! ELSE DROP THEN ;
: ПОЛ ( A--) ЭТАЖ ГОРИЗОНТАЛЬНО ;
: КРЫША 0 ПОЛ ;

И ПЕРЕМЕННЫЕ, и ФОРМУЛЫ имеют текущее численное значение по адресу >BODY CELL+ . Его и выдают при обращении "в лоб" Но формулы имеют еще и, собственно, (шитую) формулу. Кроме того, ПЕРЕМЕННАЯ "продолжает" ЭТАЖ, а ФОРМУЛА - "завершает" его, начиная новый.

: ПЕРЕМЕННАЯ ( W--)
CREATE HERE ЭТАЖ @ , ЭТАЖ ! , DOES> CELL+ @ ;
: ФОРМУЛА CREATE HERE ПОЛ 0 , 0 , ] DOES> CELL+ @ ;

Присваивание ПЕРЕМЕННОЙ нового значения с перерасчетом всех последующих ФОРМУЛ:

: ОРДЕР ( A--) >R ;
: КВАРТИРА ( A--) BEGIN ?DUP WHILE DUP>R
2 CELLS + ОРДЕР R@ CELL+ ! R> @ REPEAT ;
: => ( W--) ' >BODY DUP>R CELL+ ! R> @ КВАРТИРА ;

Пример использования:

1 ПЕРЕМЕННАЯ A
2 ПЕРЕМЕННАЯ B
ФОРМУЛА C A B + ;
4 ПЕРЕМЕННАЯ D
ФОРМУЛА E C D * ;
3 => B
\ в этот момент E уже вычислено
E . \ напечатает 16, т.е. (1+3)*4
avatar
Gudleifr
Admin

Сообщения : 879
Дата регистрации : 2017-03-29

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: Эмуляция механизма данных

Сообщение автор Gudleifr в Вс Окт 29, 2017 11:23 am

К чему это я все здесь понаписал? Не призывая подивиться на FORTH-выкрутасы. Наоборот, попытался показать, что обычная VARIABLE является FORTH-выкрутасом, а не тем, что в "нормальных языках" называют "нормальной переменной". В FORTH нет переменных!
avatar
Gudleifr
Admin

Сообщения : 879
Дата регистрации : 2017-03-29

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: Эмуляция механизма данных

Сообщение автор Спонсируемый контент


Спонсируемый контент


Вернуться к началу Перейти вниз

Вернуться к началу


 
Права доступа к этому форуму:
Вы не можете отвечать на сообщения