Таблица Умножения Prolog

02.07.2019by admin

Процедурная семантика Пролога-д ИЛИ Исполнение программы. База знаний и следующий за ней вопрос представляют собой программу на Прологе-Д. Независимо от решаемой задачи программа всегда выполняется по одной и той же схеме, определяемой методом резолюции - тем методом поиска решения, который использует система логического программирования Пролог-Д. Поиск решения осуществляется следующим образом. Среди всех элементов множества предложений S отыскивается первое предложение, которого имеет такое же имя, как и первая цель в вопросе. Если предложение - правило, то это имя должно быть именем головы, или если это предложение факт, имя факта.

  • Вот уж никогда не думала, что во взрослой жизни меня будет волновать таблица умножения.
  • Комплекс игр и тренажёров по теме 'Таблица умножения' создан для учащихся 2 - 3 класса, УМК.

Как легко запомнить таблицу умножения? Простой пошаговый алгоритм поможет вам!

Проверяется существование подстановки унифицирующей цель вопроса и это предложение. Если такая подстановка не существует, то делается попытка найти следующее по порядку предложение с таким же именем. Если такая подстановка найдена, то резольвентой оказывается тело правила, если предложение - правило или пустой дизъюнкт, если предложение факт.

Таблица Брадиса

Далее просматриваются цели в теле правила, так как если бы это были цели в вопросе. Просмотр целей осуществляется слева направо. Сущность метода резолюций можно проиллюстрировать на примере работы программы ( 2.2.

Представьте себе высокоуровневый язык, в котором не нужно указывать КАК получить результат, вместо этого нужно просто указать ЧТО вы хотите получить. При этом область применения языка не ограничена и язык способен решать те же задачи, что и любой другой высокоуровневый язык, наподобие JAVA. Кажется фантастикой, не правда ли? Однако такой язык есть и называется он PROLOG. Посмотрим как PROLOG справляется с этой задачей на примере загадывания прологу некоторых загадок и попросим PROLOG выдать доказательство теоремы. Простенькая, математическая. «?» — произвольная математическая операция (+,-,.,/), дано уравнение ((((1?2)?3)?4)?5)?6=35.

Найти неизвестные операции. Начнём описывать, что мы хотим получить. Поскольку знак операции неизвестен, он будет параметром. Formula(X1, X2, X3, X4, X5, X6, Sign1, Sign2, Sign3, Sign4, Sign5, Result):- operation(X1, X2, Sign1, PartialResult1), operation(PartialResult1, X3, Sign2, PartialResult2), operation(PartialResult2, X4, Sign3, PartialResult3), operation(PartialResult3, X5, Sign4, PartialResult4), operation(PartialResult4, X6, Sign5, Result).

Эта строка описывает формулу 1?2?3?4?5?6=Result. Фактически, она означает: формула верна, если существуют частичный результат 1, частичный результат 2 частичный результат 4, такие что верны операции. Однако пролог не знает что такое операция, поэтому опишем в каких случаях они верны: operation(X1, X2, '+', Result):- Result = X1 + X2.

Operation(X1, X2, '.' , Result):- Result = X1. X2. Operation(X1, X2, '/', Result):- Result = X1 / X2.

Operation(X1, X2, '-', Result):- Result = X1 — X2. Мы описали формулу, и теперь мы можем задавать любые вопросы относительно неё. Например, мы можем задать следующие вопросы: 1)если X1=1 X2=2 X6=6 Result=35 то какие возможны операции? 2)указана часть числовых параметров и часть операций, а также результат, узнать, каковы недостающие параметры? 3)указаны все операции, числовые параметры, результат; узнать, верна ли формула.

При этом не нужно заботиться о том, как именно пролог найдёт ответ – нужно просто задать вопрос. Итак, вопрос: askMainQuestion:- formula(1,2,3,4,5,6,Sign1,Sign2,Sign3,Sign4,Sign5,35), stdio::write(Sign1, Sign2, Sign3, Sign4, Sign5), stdio::nl,%new line fail. Ответ:., +.+-,. (Для задавания вопроса о числовых параметрах придётся написать ещё пару строк, но об этом чуть позже.). Implement main open core class predicates askMainQuestion: procedure. Operation: (real, real, string, real) multi(i,i,o,o). Formula: (real, real, real, real, real, real, string, string, string, string, string, real) nondeterm(i,i,i,i,i,i,o,o,o,o,o,i).

Abs: (real, real) nondeterm (i,o). Clauses operation(X1, X2, '+', Result):- Result = X1 + X2. Operation(X1, X2, '-', Result):- Result = X1 — X2. Operation(X1, X2, '.' , Result):- Result = X1. X2. Operation(X1, X2, '/', Result):- Result = X1 / X2.

Formula(X1, X2, X3, X4, X5, X6, Sign1, Sign2, Sign3, Sign4, Sign5, Result):- operation(X1, X2, Sign1, PartialResult1), operation(PartialResult1, X3, Sign2, PartialResult2), operation(PartialResult2, X4, Sign3, PartialResult3), operation(PartialResult3, X5, Sign4, PartialResult4), operation(PartialResult4, X6, Sign5, FinalResult), abs(FinalResult-Result, Difference), Difference=0, Result=X. Abs (X, Result):- XMan2.

(Человек1 и Человек2 братья, если существует ЧеловекРодитель, который является родителем для Человека1 и Человека2, и при этом Человек1 – это не Человек2). Brother(Man1, Man2):- parent(ChildMan1, Man1), uncle(ChildMan1, Man2). (Человек1 и Человек2 братья, если у Человека1 есть ребёнок, и Человек2 дядя этого ребёнка). Теперь зададим вопрос о том, кто является братьями: askMainQuestion:- brother(X, Y), stdIO::write(X, ' ', Y), stdio::nl, fail.

Implement main open core class predicates askMainQuestion: procedure. Parent: (string, string) multi(o,o) nondeterm(o,i) nondeterm(i,o). Brother: (string, string) nondeterm(o,o) nondeterm(i,o). Uncle: (string, string) nondeterm anyflow. Clauses parent(«Tom», «Jake»).

Parent(«Jim», «Jake»). Parent(«Timmi», «Tom»). /.uncle(Man1, Man2):- parent(Man1, ParentMan1), brother(ParentMan1, Man2). Расскомментирование этой строки уронит программу./ brother(«Timmi», «Cartman»). Brother(Man1, Man2):- parent(ChildMan1, Man1), uncle(ChildMan1, Man2).

Brother(Man1, Man2):- parent(Man1, Parent), parent(Man2, Parent), Man1Man2. AskMainQuestion:- brother(X, Y), stdIO::write(X, ' ', Y), stdio::nl, fail.

Run:- console::init, askMainQuestion= stdIO::readchar. End implement main goal mainExe::run(main::run). Вывод программы: Timmi Cartman, Jake Peter, Tom Jim, Jim Tom. Сравните с тем, какое количество кода пришлось бы написать на императивном языке программирования. Теперь давайте рассмотрим что-нибудь посложнее Hello World-ных программ и поговорим о подводных камнях пролога. На шахматной доске стоит 8 ферзей.

Ни один ферзь не бьёт другого ферзя. Найти расположение ферзей. Сначала опишем как может ходить ферзь: attack(X1, Y1, X2, Y2):- X2 = X1.%ферзь может атаковать, если атакуемая клетка и исходная на одной вертикали attack(X1, Y1, X2, Y2):- Y2 = Y1.% ферзь может атаковать, если атакуемая клетка и исходная на одной горизонтали attack(X1, Y1, X2, Y2):- abs(X2 — X1, Abs), abs(Y2 — Y1, Abs).% на одной диагонали Теперь укажем в каких случаях ферзь не может бить другого ферзя: any(0). DontAttack(X1, Y1, X2, Y2):- any(X1), any(Y1), any(X2), any(Y2), not(attack(X1, Y1, X2, Y2)). Тут возникает вопрос, зачем нужно перечисление всех возможных координат ферзя (any). Дело в том, что пролог устроен так, что он не может перебирать все возможные числовые или строковые значения.

Все возможные значения величины, относительно которой задаётся вопрос, должны быть или перечислены в коде (как например, знаки в примере про нахождение знаков), или напрямую вычисляться на основе входных данных в вопросе (как например, результат формулы в примере про нахождение знаков, если в формуле не учитывать ошибки округления). Конечно, такое ограничение делает пролог не самым удобным языком для решения уравнений; однако, это никогда и не было основным предназначением этого языка. Итак, мы описали, что означает что «один ферзь не бьёт другого ферзя», теперь нужно указать что каждый из 8 ферзей не бьёт другие 7 ферзей, однако писать 8.7=56 правил утомительно, поэтому опишем это правило рекурсивно. Зададим пустой массив и будем итеративно по одному добавлять в него по одному ферзю. (если нет ферзей, то никакой ферзь не бьёт никакого другого) dontAttack(X, Y, ):- any(X), any(Y). (если есть один единственный ферзь, то он не бьёт других ферзей) dontAttack(X1, Y1, X2, Y2 OtherElements):- dontAttack(X2, Y2 OtherElements), dontAttack(X1, Y1, X2, Y2), dontAttack(X1, Y1, OtherElements). (если есть ферзь с координатами X1 и Y1 и массив ферзей, то ни один ферзь не бьёт другого, если 1) внутри массива ферзей ни один ферзь не бьёт другого 2)ферзь (X1,Y1) не бьёт первого ферзя из массива ферзей 3)если убрать из массива ферзей первого ферзя, то в полученном множестве ферзей ни один ферзь также не бьёт другого ) dontAttack(X1, Y1 OtherElements):- dontAttack(X1, Y1, OtherElements).

(если есть ферзь с координатами X1 и Y1 и массив ферзей, и ни один ферзь не бьёт другого, то если добавить ферзя в массив ферзей, то ни один ферзь по-прежнему не будет бить другого) Теперь осталась задать вопрос о координатах этих ферзей. Однако, чтобы не получить множества одинаковых ответов, отличающихся лишь нумерацией ферзей, давайте скажем, что первый ферзь стоит в первом столбце, второй – во втором, и т.д.: askMainQuestion:- X1=0, X2=1, X3=2, X4=3, X5=4, X6=5, X7=6, X8=7, dontAttack (X1, Y1, X2, Y2, X3, Y3, X4, Y4, X5, Y5, X6, Y6, X7, Y7, X8, Y8), stdio::write(X1, ':', Y1, ' — ', X2, ':', Y2, ' — ', X3, ':', Y3, ' — ', X4, ':', Y4, ' — ', X5, ':', Y5, ' — ', X6, ':', Y6, ' — ', X7, ':', Y7, ' — ', X8, ':', Y8), stdio::nl,%new line fail. Запускам программу и в ту же секунду получаем все возможные расстановки ферзей на шахматной доске. Implement main open core class predicates askMainQuestion: procedure. DontAttack: (integer, integer, integer, integer) nondeterm anyflow. Attack: (integer, integer, integer, integer) nondeterm(i,i,i,i).%nondeterm anyflow. Any:(integer) multi(o) determ(i).

DontAttack: (integer, integer, integer.) nondeterm anyflow. DontAttack: (integer.) nondeterm anyflow. Abs: (integer, integer) nondeterm (i,o) nondeterm (i,i). Clauses any(0).

Attack(X1, Y1, X2, Y2):- X2 = X1. Attack(X1, Y1, X2, Y2):- Y2 = Y1.

Attack(X1, Y1, X2, Y2):- abs(X2 — X1, Abs), abs(Y2 — Y1, Abs). DontAttack(X1, Y1, X2, Y2):- any(X1), any(Y1), any(X2), any(Y2), not(attack(X1, Y1, X2, Y2)). DontAttack(X1, Y1, X2, Y2 OtherElements):- dontAttack(X2, Y2 OtherElements), dontAttack(X1, Y1, X2, Y2), dontAttack(X1, Y1, OtherElements).

DontAttack(X, Y, ):- any(X), any(Y).%Граничное условие dontAttack(X1, Y1 OtherElements):- dontAttack(X1, Y1, OtherElements). AskMainQuestion:- X1=0, X2=1, X3=2, X4=3, X5=4, X6=5, X7=6, X8=7, dontAttack (X1, Y1, X2, Y2, X3, Y3, X4, Y4, X5, Y5, X6, Y6, X7, Y7, X8, Y8), stdio::write(X1, ':', Y1, ' — ', X2, ':', Y2, ' — ', X3, ':', Y3, ' — ', X4, ':', Y4, ' — ', X5, ':', Y5, ' — ', X6, ':', Y6, ' — ', X7, ':', Y7, ' — ', X8, ':', Y8), stdio::nl,%new line fail.

Abs(X, Result):- X=0, Result=X. Abs(X, Result):- X«E», NotB«M», operation(A, NotB, C), ofGroup(A, «SubSet»), ofGroup(B, «SubSet»), stdio::write(C, ' is from subset, because ', A, '.' , NotB, '=', C, '. '), stdio::nl. Теперь в качестве NotB нельзя использовать «E» и «M», и программа не повиснет (да-да, и на «M» она тоже повисает). После этого программа в ту же секунду выдаёт доказательство трёх пунктов теоремы: E is from subset, because B.notB=E.

E is from subset (A и B лежат в подмножестве, поэтому произведение A на обратный к B лежит в подмножестве. В данном случае вместо A и B взят один и тот же элемент «B». Вывод доказательства можно сделать более подробным, если добавить больше команд write в правила) E is from subset, because B.notB=E. NotA is from subset, because E.notA=notA. NotA is from subset E is from subset, because B.notB=E. NotB is from subset, because E.notB=notB. AB is from subset, because A.B=AB.

A.B is from subset. Implement main open core domains any = string. Group = string. Class predicates firstQuestion: procedure. SecondQuestion: procedure.

ThirdQuestion: procedure. Operation: (any, any, any) nondeterm(i,i,i) nondeterm(o,i,i) nondeterm(i,i,o) nondeterm(o,o,i) nondeterm(o,o,o) nondeterm(i,o,i) nondeterm(i,o,o) nondeterm(o,i,o). Obratni: (any, any) nondeterm(i,i) nondeterm(o,i) nondeterm(o,o) nondeterm(i,o).

OfGroup: (any, group) nondeterm(i,i) nondeterm(o,i) nondeterm(i,o). Any: (string) nondeterm(i) nondeterm(o). Clauses operation(A, B, «E»):- obratni(A, B). Operation(B, A, «E»):- obratni(A, B). Operation(A, «E», A):- any(A). Operation(«E», A, A):- any(A).%коммутативность относительно нейтрального и обратного является следствием из ассоциативности, но не входит в миниммальное определение группы operation(«A», «B», «AB»).%умножение А на В возможно obratni(«M», «notM»).

Obratni(«notM», «M»). Obratni(«B», «notB»). Obratni(«notB», «B»). Obratni(«A», «notA»). Obratni(«notA», «A»).

Obratni(«E», «E»). OfGroup(X, «Set»):- ofGroup(X, «SubSet»).%Определение подмножества ofGroup(«E», «Set»). OfGroup(«M», «Set»).

OfGroup(«A», «SubSet»). OfGroup(«B», «SubSet»). OfGroup(«notB», «Set»). OfGroup(«notA», «Set»).

OfGroup(C, «SubSet»):- obratni(B, NotB), NotB«E», NotB«M», operation(A, NotB, C), ofGroup(A, «SubSet»), ofGroup(B, «SubSet»), stdio::write(C, ' is from subset, because ', A, '.' , NotB, '=', C, '. '), stdio::nl.% a(-b) э Set firstQuestion:- ofGroup(«E», «SubSet»), stdio::write(«E is from subset»),! FirstQuestion:- stdio::write(«E is NOT from subset»). SecondQuestion:- ofGroup(«notA», «SubSet»), stdio::write(«notA is from subset»),!

SecondQuestion:- stdio::write(«NotA is NOT from subset»). ThirdQuestion:- operation(«A», «B», AB), ofGroup(AB, «SubSet»), stdio::write(«A.B is from subset»),! ThirdQuestion:- stdio::write(«A.B is NOT from subset»).

Run:- console::init, firstQuestion, stdio::nl,stdio::nl,stdio::nl, secondQuestion, stdio::nl,stdio::nl,stdio::nl, thirdQuestion= stdIO::readchar. End implement main goal mainExe::run(main::run). Вывод Prolog – замечательный декларативный язык, однако постоянные повисания не делают ему чести (если кто-нибудь знает способы борьбы с этим, просьба поделиться в комментариях).

В самом деле, задача поиска циклов в графах решена давным давно, и устранение проблемы на уровне языка — вполне решаемая задача! Если бы эта проблема была решена, можно было бы составить базу знаний всех известных математических фактов, и задавать любые вопросы! Или например, изменить одну из базовых аксиом математики и в моментально узнать как изменятся другие законы (а-ля в мгновение ока)! Конечно, я настроен несколько оптимистично, и языку требуется немного больше доработок, но всё же Например, одна из возможных доработок: Prolog должен уметь не только выходить из зацикливания, но и уметь обрабатывать зацикливания по определённым правилам, чтобы оперировать с бесконечными последовательностями, удовлетворяющими условию теоремы, как например в теореме. Впрочем, в текущем состоянии Prolog врят ли пригоден вообще для чего бы то ни было: для поиска причин зацикливаний у меня ушло даже больше времени, чем для доказательства самой теоремы! (Имхо, всего лишь моё мнение) Метки:. Добавить метки Пометьте публикацию своими метками Метки лучше разделять запятой.

Например: программирование, алгоритмы. Нужно понимать, что «классический» Пролог просто перебирает все варианты подряд поиском в глубину с возвратами. Тех же ферзей на большой доске не расставишь.

Логика

Современные системы, основанные на Прологе, гораздо умнее. Главное достижение — constraint logic programming, которое использует интересные алгоритмы для отсечения больших частей дерева поиска. Как пример использования — мое решение Greater Than Sudoku: Еще есть такая интересная вещь, как tabling — вариант эффективной автоматической мемоизации логических программ, использование которого позволяет избегать «зацикливания» и декларативно реализовывать алгоритмы динамического программирования. Greater Than Sudoku — это не обычное Sudoku, а совсем другая головоломка.

Да и для решения обычного Sudoku, как учит нас Питер Норвиг, надо реализовать constraint propagation и поиск. Это не очень сложно в случае Sudoku, но в поддерживающих constraint logic programming системах уже реализовано и отлажено.

Для Какуро вот решения от Hakan Kjellerstrand: (ECLiPSe) и (B-Prolog). Для Забора с ходу не найду основанных на Прологе решений, но вроде бы задача идеально подходит для constraint logic programming. Попробую написать решение, как будет время.

Да и для решения обычного Sudoku, как учит нас Питер Норвиг norvig.com/sudoku.html, надо реализовать constraint propagation и поиск.Пара умных терминов и все выглядит уже по-научному. Не знаю насчет Норвига, но простейшая рекурсия с судоку работает на ура. Greater Than Sudoku — это не обычное Sudoku, а совсем другая головоломка. Просто в способе программирования от обычного судоку она ничем существенным не отличается. Для Какуро вот решения от Hakan Kjellerstrand Спасибо.

Теперь вот непонятно, у себя что ли пролог развернуть. Хочется посмотреть, как это будет на сетке нормального размера отрабатывать. А то для приведенного примера вручную решить быстрее, чем текст условия набивать:) А может быть где-то в сети есть возможность побаловаться? Процитирую тут: server(Port):- tcpsocket(Socket), tcpbind(Socket, Port), tcplisten(Socket, 5), tcpopensocket(Socket, In, Out), addstreamtopool(In, accept(Socket)), streampoolmainloop. Accept(Socket):- tcpaccept(Socket, Slave, Peer), tcpopensocket(Slave, In, Out), addstreamtopool(In, client(In, Out, Peer)). Client(In, Out, Peer):- readlinetocodes(In, Command), close(In), format(Out, 'Please to meet you: sn', Command), close(Out), deletestreamfrompool(In).

Обычный императивный язык, в котором операторы разделяются запятыми. Ну, это только с первого взгляда так кажется.

В Prolog-е нет таких понятий, как присвоение переменных и вызов функций. Всё описывается в виде отношений между сущностями и безусловных фактов о сущностях. Например, A = stdio::read концептуально означает «существует A, совпадающая с тем что ввёл пользователь в консоль», это не присвоение переменной. Точно также, operation(X1, X2, '+', Result):- Result = X1 + X2 — это не вызов функции и не присвоение Result суммы X1 + X2. Почитайте статью (я уверен, что вы её не читали), там в первом примере есть описание отношения формулы и операций, с первого взгляда может показаться, что это обычный вызов функции, в котором операторы разделяются запятыми, но по ходу разбора примера становится очевидно, что это не так. Да его нельзя описать. Так же как нельзя доказать следующее правило.

B:- not(A).% A or B, можно записать not(A):-B. Но в методе линейной резолюции обычно пишем (A;B). B.:- A (fail, хотя не должен ).

Если бы мы записали это в логическом выражении то это эквивалентно Пример может быть очень простой. Математическая индукция.

F(X):- f(1), (f(N) - f(N+1)). Или что равносильно или что равносильно f(N); f(X):- f(1), f(N+1). В общем-то, что означает, что никакую теорему методом индукции мы не докажем, даже если сможем поддерживать конструкцию высшего порядка, добавляя это правило ко всем формулам.

Хотя метод резолюций применим для данных случаев. Implement main open core class predicates man:(string) nondeterm anyflow. AOutput:(string) nondeterm anyflow. A: (string) nondeterm anyflow. B:(string) nondeterm(i) nondeterm(o). NotB:(string) nondeterm(i) nondeterm(o).

NotBOutput:(string) nondeterm(i) nondeterm(o). Conflict:(string) nondeterm anyflow. ConflictOutput:(string) nondeterm anyflow. Ux-дизайн. практическое руководство по проектированию опыта взаимодействия. Question: procedure.

Clauses man(«Smith»). NotB(X):- man(X), not(a(X)).

NotBOutput(X):- man(X), not(aOutput(X)). Conflict(X):- b(X), notB(X). AOutput(X):- man(X), not(a(X)), conflict(X).%A истинно, если ложность A приводит к противоречию aOutput(X):- a(X). /.Дробление A на a и aOutput возникло из-за проблем с зацикливанием, о которых я уже писал. Далее, если нужно указать почему A истинно, то следует использовать a, во всех остальных случаях — aOutput./ conflictOutput(X):- b(X), notBOutput(X).

Question:- aOutput(X), not(conflictOutput(X)), stdio::write(«A true for », X), stdio::nl, fail. Run:- question, stdio::write(«End»)= stdio::readChar. End implement main goal console::runUtf8(main::run). Вывод программы: A true for Smith A true for Johnson Пример 2 1)Если А истинно, то B ложно 2)А истинно относительно «Johnson» 3)А истинно относительно «Smith» 4)B истинно относительно «Smith».

Implement main open core class predicates man:(string) nondeterm anyflow. AOutput:(string) nondeterm anyflow. A: (string) nondeterm anyflow. B:(string) nondeterm(i) nondeterm(o). NotB:(string) nondeterm(i) nondeterm(o).

NotBOutput:(string) nondeterm(i) nondeterm(o). Conflict:(string) nondeterm anyflow. ConflictOutput:(string) nondeterm anyflow. Question: procedure.

Русский Язык

Clauses man(«Smith»). NotB(X):- a(X). NotBOutput(X):- aOutput(X).

Conflict(X):- b(X), notB(X). AOutput(X):- man(X), not(a(X)), conflict(X).%A истинно, если ложность A приводит к противоречию aOutput(X):- a(X). /.Дробление A на a и aOutput возникло из-за проблем с зацикливанием, о которых я уже писал. Далее, если нужно указать почему A истинно, то следует использовать a, во всех остальных случаях — aOutput./ conflictOutput(X):- b(X), notBOutput(X). Question:- aOutput(X), not(conflictOutput(X)), stdio::write(«A true for », X), stdio::nl, fail. Run:- question, stdio::write(«End»)= stdio::readChar.

End implement main goal console::runUtf8(main::run). Вывод программы: Только «A true for Johnson», потому что истинность A для «Smith» приводит к противоречию. Имхо, если у вас когда-то что-то не получилось, то это не повод заявлять, что это принципиально невозможно и вводить людей в заблуждение. Я думаю, «принципиально» слово имеет не негативный, а концептуальный. То, что not иногда выдает «правильные» результаты, абсолютно не делает его логичным. Я советую вернуться к проблеме доказательства теорем, которая гораздо полнее раскрывает Пролог, и попытаться доказать хоть одну используя принцип индукции. По поводу самого отрицания это не мои выдумки, во-первых подверждения отсутствия отрицания и его введения (Negotation as failure) (там же Note that the above example uses true mathematical negation, which cannot be expressed in Prolog.).

Да и подумайте логически, известно что Пролог поддерживает только Хорновские дизъюнкты! Если бы он покрывал все случаи, имело бы смысл вводить разные виды Линейных резолюций для доказательства теорем. Вот здесь опять же идет такой же вопрос (и приводится ссылка на (See Mark Stickel's papers on his «Prolog Technology Theorem Prover» for one approach.). Not(A(X)) работает следующим образом: если prolog не может доказать верность A(X), то not(A(X)) считается истинным, в противном случае — ложным.

Это поведение отличается от классического инвертирования булевой переменной, поэтому not в логическом программировании получил название «negation as failure». Нашёл по приведённой вами ссылке следующий: In Planner, negation as failure could be implemented as follows: if (not (goal p)), then (assert ¬p) which says that if an exhaustive search to prove p fails, then assert ¬p.1 Note that the above example uses true mathematical negation, which cannot be expressed in Prolog. Что переводится как Negation as failure (то есть not) может быть написан на так: if (not (goal p)), then (assert ¬p), что означает: если не удалось доказать истинность p, тогда говорим что отрицание от p истинно (под отрицанием здесь понимается инвертирование булевой переменной). Обратите внимание, что описанный выше пример использует классическое математическое отрицание (то есть инвертирование булевой переменной), которое невозможно описать на прологе. То есть тут описывается как работает в Prolog-е not и говорится, что это не тоже самое, что и инвертирование булевой переменной, что вполне логично: в Prolog-е нет присвоения переменных, следовательно нет и инвертирования переменных. Говорится следующее: The logical status of negation as failure was unresolved until Keith Clark 1978 showed that, under certain natural conditions, it is a correct (and sometimes complete) implementation of classical negation with respect to the completion of the program. Что переводится как Статус negation as failure (то есть not) был непонятен до тех пор, пока Кейф Кларк в 1978 не доказал, что not это правильная реализация классического отрицания.

Таким образом, в приведенном вами источнике сказано, что not — это логическое отрицание. По поводу примера, который вы привели: уверен, что Prolog скажет, что A истинно. Prolog не сможет доказать истинность B (т.к. Не описано правило по которому оно истинно), следовательно not(B) истинно, из чего следует истинность A согласно правилу 1. Могу написать код, если у вас остались сомнения. Надеюсь, у вас хватит энтузиазма разобраться и написать статью. Погуглил про Fuzzy Prolog.

Большая часть результатов — состоит из ссылок на статью «Fuzzy Prolog: A Simple General Implementation Using CLP®». Попытки найти интерпретатор или компилятор ведут на тему www.computer-programming-forum.com/55-prolog/e842ceba994dadd9.htm из единственного сообщения без ответа. Похоже мне чтобы попробовать Fuzzy Prolog нужно будет сначала написать самому эмулятор этого языка (смогу взяться за это только летом). Вы акцентируете внимание на несущественном. На пролог очень компактными (и понятными!) получаются комбинаторные решения. Те же шашки, например. Речь о том, что «любая серьезная программа содержит в себе LISP интерпретатора и код на LISP».

Если вы будете писать переборный алгоритм, например на Си, вам придется написать свою реализацию Prolog, чаще урезанную, и еще код ограничений для перебора всех вариантов. Так зачем писать этот код, когда можно взять готовый Prolog и написать только ограничения перебора?

С точки зрения практического использования — напротив существенно! Пользователю — абсолютно не важно, как и что внутренне реализовано. Пользователь — желает иметь дружественный интерфейс и удобное представление результатов. Я не могу на SQL создать нормальный пользовательский интерфейс. Но, я могу из внешней программы с нормальным пользовательским интерфейсом обратиться к SQL-серверу, и отправив туда SQL-запрос, получить нужные результаты, а затем посредством внешней программы представить полученные результаты в нужной пользователю форме. А я могу через внешнюю программу с интерфейсом точно также обратиться к Prolog-серверу?

Еще один перспективный подход в декларативной архитектуре — это логическое про­граммирование (rule-based programming) с механизмом выводов и базой правил. К сожалению, и эта идея может провалиться из-за некоторых тонкостей.

Хотя программа, основанная на совокупности логических правил, в принципе декларативна, в большинстве таких систем имеются «управляющие предикаты», добавленные туда для возможности настройки производительности. Этот управляющий код вносит побочные эффекты, и в результате поведение программы уже не полностью определяет­ся декларированными правилами. Добавление, удаление и переупорядочение правил может привести к непредсказуемым и неправильным результатам. Поэтому программист в логической парадигме, как и программист объектно-ориентированный, должен тщательно следить за очевидностью и предсказуемостью поведения кода. Похоже, Эрик Эванс знал промышленные проекты, в которых использовалась логическая парадигма, так как книга посвящена проектированию программ, работающих со сложной предметной областью. Начало цитаты ru.wikipedia.org/wiki/%D0%94%D0%A0%D0%90%D0%9A%D0%9E%D0%9D Дружелюбный русский алгоритмический язык, который обеспечивает наглядность (сокр.

ДРАКОН) — визуальный алгоритмический язык программирования и моделирования5. Был разработан в рамках космической программы «Буран». Разработка языка велась с 1986 года при участии Федерального космического агентства (Научно-производственный центр автоматики и приборостроения им. Пилюгина, Москва) и Российской академии наук (Институт прикладной математики им. Язык построен путём формализации, эргономизации и неклассической структуризации блок-схем алгоритмов, описанных в ГОСТ 19.701-90 и ISO 5807-85, а также для разработки программ реального времени конец цитаты То есть ПО для Бурана совсем не похоже на Пролог, а представляет императивный язык основанный на теории конечных автоматов.

SQL появился чуть позже Prolog. Похоже, авторы не знали пролога. Раз в десять лет заседали и принимали решения о развитии SQL. Если не ошибаюсь, в 80х и 90х годах в решениях этой комиссии одним из 10 пунктов были пункты «двигаться в сторону Prolog».

В частности, в стандартах 2000х в SQL была введена рекурсия. Сегодня SQL уже не торт. Например, LINQ выигрывает у него в автоподстановке имен полей таблицы, т.к.

Тип таблицы известен до обращения к полям. В SQL сперва записываются поля (по памяти), а уже потом таблица (из которой можно выбрать поля). Вот буквально пару дней назад как сдал экзамен по логическому программированию/программированию в ограничениях, в рамках которого мы изучали язык Oz. Пролог изучал годом раньше.

Oz гораздо более интересный и продвинутый мультипарадигменный язык, в котором есть человеческие структуры данных, хорошо реализована функциональная парадигма, можно самому описать стратегию обхода графа, встроенный визуализатор графа решений и много других плюшек. Настоятельно рекомендую ознакомиться всем вдохновившимся прологом. Prolog можно изучать ступенчато. Есть вещи очень простые. Вникаешь — уже можешь кодировать. Инструкция по охране труда рф. Затем через некоторый момент осознаешь, что не понимаешь, как ЭТО работает.

Затем проникаешься, переходишь на еще один уровень и т.д. «до мага 89 уровня»:) Уровень 1. Принять Унификацию. (в обе стороны!) Уровень 2. Программа на прологе это База Данных + Запросы Уровень 3. Унификация работает в обе стороны!!!

И в запросах!!! В попытке разрешения успеха или неуспеха унификации запросов может уйти в бесконечность (stack overflow) Уровень 5. Даже если нет бесконечности, есть проблема комбинаторного взрыва. Перебирать всё нет смысла.

Есть удачный порядок перебора, а есть очень затратный. Оператор отсечения может быть полезен. И даже очень. Но с его использованием нужно перестроить систему знаний о Prolog. Тормоз Уровень 8.

Оказывается, Prolog очень хорошо масштабируется! Можно по ядрам, а можно и в кластере. Простой логический решатель. Простой геометрический решатель Уровень 25. Системы компьютерной алгебры. Решатель способен поступить в любой университет (сдать ЕГЭ) Уровень 42.

Гугол не так и много:). Пояснение по борьбе с исключениями путём модификации компилятора fact(2). P(N):- fact(I), IN, p(N). P(N):- stdio::write(N).

Проще будет понять объяснение, если одновременно читать и трассировать программу. Допустим в goal задали Prolog-у вопрос, истинно ли p(10). В таком случае не будет зацикливания. Prolog заходит в строку 3, в контейнер (например, в красно-черное дерево) кладём объект, состоящий из информации об обходе предиката — p(10), а также хэша этой информации. Дальше видим fact(I). Кладём в контейнер fact(output).

Видим факт fact(2). Возвращаемся из fact(I) и одновременно достаём из контейнера fact(output).

Далее IN, которое не выполняется (2 10 ложно). Далее пролог возвращается в fact(I) и начинает перебирать остальные возможные I. Снова кладем в контейнер fact(output). Видим fact(4).

Возвращаемся из fact(I) и одновременно достаём из контейнера fact(output). Видим что IN — ложно. Далее p(N):- stdio::write(N), пролог выводит 10. Достаём из контейнера p(10). Программа завершается. Теперь давайте разберём случай, когда зацикливание происходит. В goal задали вопрос истинно ли p(1).

Логика

Поначалу выполнение программы похоже. Кладём в контейнер p(1), кладём fact(output), достаём fact(output) и теперь I=2. Теперь снова пытаемся положить в контейнер p(1), но видим что в контейнере есть объект с таким же хэшем, сравниваем сами объекты и видим что они равны. Обнаружили зацикливание. Кидаем исключение.

Перехватываем исключение там где происходит ближайший перебор значений, то есть при переборе fact(I). Продолжаем перебор. Теперь I=4, снова зацикливание, снова кидаем исключение и перехватываем на переборе fact(I). Prolog видит что возможные значения I закончились, и продолжает программу, после чего выводит 1 и корректно завершается. Полное обсуждение. В ходе обсуждения выявился любопытный частный случай, нуждающийся в дополнительной обработке.

В Prolog-е можно дописывать удалять предикаты во время исполнения программы посредством команд assert и retract. При добавлении нового предиката повторное захождение в некий предикат не всегда говорит о зацикливании, ведь программа может повести себя совсем по-другому. Чтение с консоли, чтение из файла, генерирование случайного числа похоже на то, как если бы мы добавили предикат, описывающий поступившие данные в виде факта, с последующим его удалением. Давайте представим себе воображаемую ситуацию: программист и Капитан Очевидность собрались вместе, чтобы устранить этот недочёт в алгоритме. П: Алгоритм считает, что если программа повторно зашла в предикат с теми же самыми параметрами, то программа зациклилась. Когда добавляются новые правила, алгоритм продолжает считать также, но это неверно.

КО: А пусть он так не считает! П: Хорошо, после того как добавились новые предикаты, алгоритм не будет считать что захождение в тот же самый предикат привело к зацикливанию. Но как добиться того, чтобы алгоритм перестал так считать? КО: Для того, чтобы ответить на этот вопрос, нужно знать подробности вашего алгоритма. П: Ну, если вкратце, мы кладём в контейнер запись с пометкой что мы обходили предикат. Потом на основании наличия пометки решаем, произошло ли зацикливание.

Есть пометка — считаем что есть зацикливание, нет пометки — нет зацикливания. КО: Ну так сделайте так, чтобы не было пометки! П: Хорошо, при добавлении нового предиката удаляем пометки из контейнера. Но какие пометки нужно убрать? Убирать все пометки нецелесообразно.

Добавление нового предиката может оказывать влияние не на все другие предикаты, а лишь на некоторые из них. КО: Ну так вы и убирайте только те, на которые добавление нового предиката могло оказать влияние! П: Спасибо, Капитан! (в диалоге описано как устранить недочёт в алгоритме). Ещё примеры алгоритма борьбы с зацикливаниями. P:- consult(«name.txt», имяБД), retract(fact(N,A,B)), asserta(fact(random(9),A,B)), fail; write(«end»). Кладём в контейнер пометку об обходе consult(«name.txt», имяБД).

Consult добавляет в программу новые предикаты; для каждого добавленного предиката извлекаем из контейнера пометки о тех предикатах, поведение которых изменилось после добавления нового предиката. Consult не «заходит» ни в один пользовательский предикат. Извлекаем consult из контейнера. Потом обходится предикат retract(fact(N,A,B)), кладём в контейнер пометку об обходе retract(fact(N,A,B)). Извлекаем из контейнера пометки обо всех предикатах, поведение которых изменилось после удаления предиката fact(N,A,B). Эти предикаты можно найти рекурсивно: это непосредственно fact, все предикаты в которых использовался fact (например, если rule:-someRule1, fact(X,Y,Z), someRule2, то поведение rule изменилось), все предикаты в которых использовались предикаты в которых использовался fact, и так далее.

Таблица Косинусов

Достаём из контейнера информацию об обходе retract(fact(N,A,B)). Кладём в контейнер информацию об обходе asserta(fact(random(9),A,B)). Извлекаем из контейнера все предикаты, поведение которых изменилось при добавлении предиката fact. Извлекаем из контейнера информацию об обходе asserta(fact(random(9),A,B)). P:- if 42 random(10000) then p end if. Кладём в контейнер информацию об обходе p.

Кладём в контейнер информацию об обходе random(10000). При вызове random, введении данных извне, добавлении новых предикатов или их удалении, нужно извлечь из контейнера все предикаты, поведение которых может отличаться при следующем их обходе. То, как найти такие предикаты было описано ранее, в данном случае это p, извлекаем пометку о p из контейнера.

Допустим random выдал 5. Извлекаем из контейнера пометку об обходе random(10000). Теперь мы снова пытаемся обойти p. В контейнере нет пометки об обходе p, поэтому мы не детектируем зацикливание и разрешаем это сделать. Помещаем в контейнер пометку об обходе p. Помещаем в контейнер пометку об обходе random(10000). Допустим random выдал 42.

Таблица Интегралов

Извлекаем из контейнера пометку об обходе random(10000). Извлекаем из контейнера пометку об обходе p. Снова пытаемся извлечь пометку об обходе p, но её нет в контейнере — ничего страшного, нет ну и ладно, продолжаем выполнение программы. P:- if -42 random(10000) then p end if.

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