Loading...
banner Исторический Prolog

В заголовке этой статьи слово Prolog написано мало того что латиницей, так еще и с заглавной буквы. Это объясняется тем, что в нем упомянут язык программирования Prolog, используемый для создания экспертных систем. Мне давно хотелось применить его к какой-нибудь гуманитарной задаче, но всё как-то не доходили руки. Во-первых потому, что сама эта затея выглядит как некая «блажь», и всегда находятся «дела поважнее».

Во-вторых, Prolog — язык необычный, требует усилий при освоении. Написанные на нем программы представляют собой не ряд последовательно выполняемых инструкций, как в других языках программирования, а некие логические уравнения. Если их правильно составить — программа сама находит ответ на тот или иной запрос. Для историков, социологов, лингвистов этот язык программирования мог бы стать незаменимым инструментом, но, к сожалению, даже среди программистов не так много людей, которые им владеют. Многие и вовсе считают его не более чем забавным раритетом.

В-третьих, большинство реализаций Prolog работают лишь с англоязычным синтаксисом, а хотелось бы писать программы, в которых при желании может разобраться русскоязычный читатель.

Понемногу все эти трудности были преодолены. Я нашел реализацию Prolog, поддерживающую инструкции на любых естественных языках, в том числе на русском. Это SWI-Prolog, продукт зрелый, так сказать «академический», обладающий необъятным количеством возможностей. Попрактиковаться в написании программ на Prolog мне тоже довелось в последнее время немало, поскольку я консультирую студентов IT-специальностей, а язык этот, оказывается, в некоторых вузах входит в учебные программы. Наконец, стремление применить Prolog к гуманитарным исследованиям с годами лишь усилилось, значит это не блажь, а некое обоснованное стремление. Исторического материала, который можно было бы обработать с помощью Prolog, накопилось у меня много.


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

Историки разделили предмет своего изучения — «ленту времени» — на характерные участки, которые знакомы даже школьникам: первобытно-общинный строй (примерно 10 тыс. лет назад и ранее), Древний мир (примерно до V в. н. э.), Средние века (примерно до XV в. н. э.), Новое время (примерно до конца XIX в.), Новейшее время (нынешнее, в рамках 3–4 последних поколений).

Деление это очень условное, спорное, с нечеткими границами, но, по крайней мере, при упоминании этих терминов все более-менее понимают, о чем идет речь. В рамках этих больших эпох выделяют «подэпохи». Так, «внутри» Древнего мира помещают Античность (VI в. до н. э. — V в. н. э.), основные события которой разворачиваются вокруг древних Греции и Рима. В Средневековье выделяют Темные века (от падения Рима до Карла Великого). Новое время включает в себя эпоху Возрождения (XIV-XVI вв.) и т. д.

Иногда для решения той или иной исторической задачи достаточно четко определить принадлежность сравниваемых событий, персон или явлений к той или эпохе, и тогда сразу становится понятным, могло такое произойти или нет. Но дело в том, что границы между эпохами размыты, особенно в контексте разных стран (где-то, например, «Средневековье» наступило раньше, где-то позже). Но даже если «очистить» эпохи друг от друга очень грубо, кое-что уже может проясниться. Например, никто не будет спорить с тем, что Античность и Новейшее время не пересекаются, иначе Юлий Цезарь мог бы оказаться современником Николая II. Попробуем выполнить такое разделение применительно, например, к эпохам Античности, Средневековья и Возрождения (это как бы «подэпоха» Нового времени, но сути не меняет).

Пересекающиеся эпохи.

 Пересекающиеся эпохи.

Допустим, некое событие относят учеными то к античности, то к Средневековью (например, что-то связанное со временами Великого переселения народов, растянувшимися на несколько столетий). Такое соотнесение будет считаться достаточно достоверным. Хотя четкой границы между соседними эпохами нет, можно найти дополнительные аргументы для классификации. Но если событие или явление идентифицируются то как происходившие в Античности, то как происходившие в эпоху Возрождения (т. е. датируется не смежными эпохами), то тут стоит поискать фальсификацию. Об этом много говорят и пишут т. н. альтернативные историки, но для внесения ясности нужна общепринятая форма записи фактов, глядя на которую все понимали бы логику аргументации. Язык Prolog, мне кажется, подходит для этого как нельзя лучше. Нужно только научить историков и других гуманитариев им пользоваться, и эта статья (а я планирую написать на эту тему еще несколько) может стать для кого-то первым шагом к достижению такой ясности.

Замечу, что публикуемые здесь примеры кода — не просто учебный материал, а реальные программы, отлаженные и работающие в среде SWI-Prolog).

Язык `Prolog`, говоря упрощенно, состоит из термов (утверждений, фактов) и правил (выражений, функций, процедур, операций над фактами, если сравнивать правила `Prolog` с обычными языками программирования). Например, сведения об эпохах и их пересечениях можно описать так:
% Факты: эпоха("Первобытность"). эпоха("Древний мир"). эпоха("Античность"). эпоха("Тёмные века"). эпоха("Средневековье"). эпоха("Возрождение" ). эпоха("Новое время"). эпоха("Новейшее время"). пересекаются(["Первобытность", "Древний мир"]). пересекаются(["Первобытность", "Античность"]). пересекаются(["Древний мир", "Античность" ]). пересекаются(["Древний мир", "Средневековье" ]). пересекаются(["Древний мир", "Тёмные века"]). пересекаются(["Античность", "Тёмные века"]). пересекаются(["Тёмные века", "Средневековье" ]). пересекаются(["Античность", "Средневековье" ]). пересекаются(["Средневековье", "Возрождение" ]). пересекаются(["Средневековье", "Новое время"]). пересекаются(["Возрождение", "Новое время"]). пересекаются(["Новое время", "Новейшее время"]). % Правила: не_пересекаются(E1, E2):- эпоха(E1), эпоха(E2), E1 \= E2, not(пересекаются([E1, E2])), not(пересекаются([E2, E1])). % Немного магии для работы со списками: найти_все(B, L):- не_пересекаются(E1, E2), format(atom(A), 'Не пересекаются эпохи "~w" и "~w".', [E1, E2]), not(member(A, B)), write(A), nl, найти_все([A|B], L),!. найти_все(B, L):- L = B,!.
Написание такой программы требует, конечно, определенного внимания, отладки. Например, в процедуре `не_пересекаются(E1, E2)` нужно позаботиться, чтобы не пересекались не только `E1` и `E2`, но и `E2` и `E1`. В отличие от современных систем искусственного интеллекта, язык программирования `Prolog`, появившийся чуть ли не в 1960-е, работает методом простого перебора.
найти_все([], _).
__Результат работы программы:__ _Не пересекаются эпохи "Первобытность" и "Тёмные века".<br> Не пересекаются эпохи "Первобытность" и "Средневековье".<br> Не пересекаются эпохи "Первобытность" и "Возрождение".<br> Не пересекаются эпохи "Первобытность" и "Новое время".<br> Не пересекаются эпохи "Первобытность" и "Новейшее время".<br> Не пересекаются эпохи "Древний мир" и "Возрождение".<br> Не пересекаются эпохи "Древний мир" и "Новое время".<br> Не пересекаются эпохи "Древний мир" и "Новейшее время".<br> Не пересекаются эпохи "Античность" и "Возрождение".<br> Не пересекаются эпохи "Античность" и "Новое время".<br> Не пересекаются эпохи "Античность" и "Новейшее время".<br> Не пересекаются эпохи "Тёмные века" и "Первобытность".<br> Не пересекаются эпохи "Тёмные века" и "Возрождение".<br> Не пересекаются эпохи "Тёмные века" и "Новое время".<br> Не пересекаются эпохи "Тёмные века" и "Новейшее время".<br> Не пересекаются эпохи "Средневековье" и "Первобытность".<br> Не пересекаются эпохи "Средневековье" и "Новейшее время".<br> Не пересекаются эпохи "Возрождение" и "Первобытность".<br> Не пересекаются эпохи "Возрождение" и "Древний мир".<br> Не пересекаются эпохи "Возрождение" и "Античность".<br> Не пересекаются эпохи "Возрождение" и "Тёмные века".<br> Не пересекаются эпохи "Возрождение" и "Новейшее время".<br> Не пересекаются эпохи "Новое время" и "Первобытность".<br> Не пересекаются эпохи "Новое время" и "Древний мир".<br> Не пересекаются эпохи "Новое время" и "Античность".<br> Не пересекаются эпохи "Новое время" и "Тёмные века".<br> Не пересекаются эпохи "Новейшее время" и "Первобытность".<br> Не пересекаются эпохи "Новейшее время" и "Древний мир".<br> Не пересекаются эпохи "Новейшее время" и "Античность".<br> Не пересекаются эпохи "Новейшее время" и "Тёмные века".<br> Не пересекаются эпохи "Новейшее время" и "Средневековье".<br> Не пересекаются эпохи "Новейшее время" и "Возрождение"._
Можно проверять отдельные утверждения:
не_пересекаются("Тёмные века", "Средневековье").
Результатом работы будет `false`.

Показать/скрыть код на языке Prolog

Стоило ли ради этого нехитрого списка, который несложно составить и вручную, «городить огород»? Конечно да, потому что, во-первых, программирование на Prolog хорошо укрепляет навыки владения формальной логикой, которыми должны в полной мере обладать не только «технари», но и гуманитарии. Не зря этот предмет входит в учебные программы истфаков, юрфаков и т. п. Во-вторых, такая программа масштабируема, т. е. при добавлении в список новых фактов она будет по-прежнему полезна, тогда как при ручной обработке длинного списка человек с высокой долей вероятности что-нибудь пропустит или перепутает, да и ошибка выяснится лишь спустя длительное время спустя, а не мгновенно, как при использовании компьютера. Наконец, созданное здесь правило для поиска непересекающихся эпох может еще не раз пригодится при решении более конкретных задач.

Таким образом, в наши дни Prolog интересен не столько в качестве экспертной системы (в этом он не может конкурировать с современным ИИ), сколько в качестве средства изложения фактов, которое можно объективно проверить с помощью компьютера.

Публикация в Telegraph