Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


ЯЗЫК ПРОГРАММИРОВАНИЯ PROLOG



А.Н. ГУЩИН

 

ЯЗЫКОВЫЕ СРЕДСТВА РАЗРАБОТКИ

ИНТЕЛЛЕКТУАЛЬНЫХ СИСТЕМ

 

 

Учебное пособие

 

Санкт-Петербург

2006

 


 

 

Гущин, А.Н.

Языковые средства разработки интеллектуальных систем: учеб. пособие / А.Н.Гущин; Балт. гос. техн. ун-т. – СПб., 2006. – с.

 

Учебное пособие содержит основные сведения о языках программирования PROLOG, LISP и CLIPS, применяемых для разработки интеллектуальных систем. Имеются примеры применения синтаксических конструкций рассматриваемых языков.

Предназначено для студентов и магистрантов, обучающихся по специальностям «Автоматизированные системы обработки информации и управления» и «Информационные системы и технологии»




ВВЕДЕНИЕ

 

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

Для разработки интеллектуальных систем могут применятся различные языковые средства и соответствующие им инструментальные средства разработки. Учитывая многообразие форм построения и областей применения современных интеллектуальных систем, в качестве языковых средств разработки могут применяться как традиционные языки программирования достаточно низкого уровня, такие как С, Pascal, Java или Basic, так и специализированные языки разработки интеллектуальных систем.

Языки программирования интеллектуальных систем – языки программирования, ориентированные на разработку интеллектуальных информационных систем, как правило имеющие парадигму, отличную от традиционных для языков программирования общего назначения процедурных парадигм.

Процедурные (императивные) парадигмы (процедурный подход) – как сделать?

Декларативные парадигмы (декларативный подход) – что мы знаем? что хотим узнать?

Промежуточное положение между процедурным и декларативным подходом занимает объектно-ориентированный подход (ООП): при рассмотрении объектов в программе, как моделей объектов реального мира – ООП можно отнести к декларативному подходу, при рассмотрении реализации поведения объектов – ООП можно отнести к императивному подходу.

К языкам программирования интеллектуальных систем можно отнести:

– LISP

– PROLOG

– CLIPS

– Haskell

– и многие другие

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

Поскольку полностью охватить весь спектр языков разработки интеллектуальных систем в рамках данного пособия не представляется возможным, рассмотрим (на самом элементарном уровне) по одному представителю трех семейств: из языков логического программирования – ЯП PROLOG, из языков функционального программирования – ЯП LISP, из специализированных языков разработки экспертных систем – язык системы CLIPS.

 

Предлагаемое учебное пособие состоит из трех разделов. Первый раздел посвящен рассмотрению принципов построения программ на ЯП PROLOG. Во втором разделе приведены базовые сведения о ЯП LISP и его использовании при разработки интеллектуальных систем. Третий раздел освещает основные возможности ЯП CLIPS, применяемого в одноименной среде разработки экспертных систем.

 


 


ЯЗЫК ПРОГРАММИРОВАНИЯ PROLOG

Основы языка Пролог

Язык программирования Пролог (PROgramming LOGic – программирование в терминах логики) предполагает получение решения задачи при помощи логического вывода из ранее известных фактов и правил. Пролог был создан в 1971 г. на факультете естественных наук в Марселе.

Программа на языке Пролог не является последовательностью действий – она состоит из описания задачи (набор фактов и правил), а Пролог-система сама строит логический вывод на основе введённых данных. В связи с этим Пролог считается декларативным языком программирования.

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

константы;

переменные;

предикаты, структуры (составные термы или функции), состоящие из имени предиката и списка аргументов-термов; имена предикатов начинаются со строчной буквы.

Константы - это поименованные конкретные объекты или отношения. Константа начинается со строчной буквы, либо заключаются в одинарные кавычки. Также константа может быть числом.

Переменные служат для обозначения объектов, значения которых меняются в ходе выполнения программы. Имена переменных начинаются с заглавных букв или знака «_» Область действия переменной – предложение. Одноименные переменные в разных предложениях могут иметь разные значения.

Специальным знаком «_» обозначается анонимная переменная, которая используется тогда, когда конкретное значение переменной не существенно для данного предложения. Значение анонимной переменной не выводится на печать.

Предикат – это единый объект состоящий из совокупности других объектов, называемых компонентами. Компоненты в свою очередь могут быть также предикатами. Название предиката стоит перед скобками, а компоненты внутри скобок перечисляются через запятую. Название предиката – функтор.

владелец (иван, книга(война и мир)).

функтор различается двумя параметрами: именем и числом параметров

point( X, Y, Z) и point(X, Y) – разные предикаты

point/3 – это означается, что у предиката point 3 аргумента,

point/2 – это означается, что у предиката point 2 аргумента.

Комментарии

Комментарии в программе помещаются между /* и */ (для любого количества строк), либо после символа % на той же строчке.

Унификация переменных.

Унификация – процесс сравнения набора условий, содержащихся в правиле, с заданными условиями, содержащимися в базе данных.

Установление соответствия между термами является основной операцией при вычислении цели. Она осуществляется следующим образом: на каждом шаге выбирается очередной терм и отыскивается соответствующее выражение в БД. При этом переменные получают или теряют значения. Этот процесс можно описать в терминах текстуальных подстановок: «подставить терм t вместо переменной Y». Свободными переменными в Прологе называются переменные, которым не были присвоены значения, а все остальные переменные называются связанными переменными. Переменная становится связанной только во время унификации, переменная вновь становится свободной, когда унификация оказывается неуспешной или цель оказывается успешно вычисленной. В Прологе присваивание значений переменным выполняется внутренними подпрограммами унификации. Переменные становятся свободными, как только для внутренних подпрограмм унификации отпадает необходимость связывать некоторое значение с переменной для выполнения доказательства подцели.

Правила унификации.

1. Если x и y-константы, то они унифицируемы, только если они равны.

2. Если x- константа или структура, а Y-переменная, то они унифицируемы, при этом Y принимает значение x .

3. Если x и y – предикаты, то они унифицируемы тогда и только тогда, когда у них одинаковые имена (функторы) и набор аргументов и каждая пара аргументов унифицируемы.

Пример:

Следующие предикаты унифицируемы

a(b, C, d(e, F, g(h, i, J))) и a(B, c, d(E, f, g(H, I, j)))

при этом B=b, C=c, E=e, F=f, H=h, I=I, J=j.

Пример:

Следующие предикаты унифицируемы

triangle(point(X, 5), point(X, 8),

point(5, Z))

triangle(point(2, 5), point(Y, 8),

point(5, A))

При этом X=2, Y=2, а переменные Z и A – неконкретизованы, но становятся сцепленными. Если две переменные сцеплены, то при конкретизации одной из них, второй переменной автоматически будет присвоено то же самое конкретное значение, что и первой.

В прологе операция = кроме сравнения выполняет сопоставление двух термов, с конкретизацией переменных. Если термы согласуются, то результат истина.

Пример:

?- a(B,c(x,D))=a(d,c(X,3)).

Yes

B=d X=x D=3

Аналогично, /= дает истину, если термы не согласуются.

Пример:

?-a(x)/=a(d).

Yes

Предикат not (отрицание)

Предикат not (<утверждение>) успешен, если утверждение ложно.

Списки

Список – это набор термов, следующих друг за другом. Пустой список [ ]. Пример непустого списка: [a,b,c,d]. Список состоит из головы и хвоста, где голова – это первый элемент списка, хвост – это список без первого элемента. Операция деления списка на голову и хвост обозначается при помощи вертикальной черты.

Пример:

?-[1,2,a,b]=[H|T].

H=1

T=[2,a,b]

?[]=[H|T].

No

В среде Turbo Prolog, имеющий явную типизацию, для использования списков необходимо явно объявлять в разделе domains соответствующий домен через домен элемента списка и символ *:

Пример:

domains

strlist=string*

В конкретных реализациях языка (конкретных средах разработки) может присутствовать ряд встроенных предикатов для работы со списками. Например, member( Elem, List), который успешен, если элемент Elem входит в список List, append( List1, List2, List3) – успешен, если список List3 является объединением первых двух списков и delete( L1, Elem, L2) – удаляет элемент Elem из списка L1 и сопоставляет полученный список с L2.

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

?-member(3,[1,2,3]).

Yes.

?-member(X,[1,2,3]),X<2.

X=1

?-append([1,2],[2,3],L).

L=[1,2,2,3]

?-append(X,Y,[1,2,3]).

X=[]

Y=[1,2,3]

X=[1]

Y=[2,3]

X=[1,2]

Y=[3]

X=[]

Y=[1,2,3]

?-delete([1,2,3],2,L).

L=[1,3]

При отсутствии встроенных предикатов для работы со списками, они могут быть легко реализованы самостоятельно, например, как показано ниже:

member(Name, [Name|_]).

member(Name, [_|Tail]):-

member(Name, Tail).

При необходимости, легко могут быть реализованы и другие операции обработки списков:

length([], 0). /* предикат

для нахождения количества

элементов в списке */

length([X|L], N):-

length(L, M), N is M+1.

Следует отметить, что так определенный предикат length, если при обращении к нему вторым аргументом используется переменная, уже связанная с некоторым целым числом, будет успешно доказан, только если оно соответствует длине списка, переданного первым аргументом.

write_list([])./* предикат

для вывода элементов списка

на экран */

write_list([H|T]):-

write(H),nl,write_list(T).

read_list([X|T]):-

write('Введите элемент: '),

read(X), X\=end,!,

read_list(T). %ввод списка

 

Откат (backtrace)

Откат – это поиск с возвратом, в результате которого находятся все варианты решения.

Пример:

большой(медведь).

большой(слон).

маленький(кот).

бурый(медведь).

черный(кот).

серый(слон).

темный(Z):-черный(Z).

темный(Z):-бурый(Z).

?-темный(X),большой(X).

При непосредственном обращении к Пролог-системе (в командной строке интерпретатора) производится поиск всех возможных решеений с использованием механизма отката до точки (предиката), для которого ещё остались ранее не рассмотренные решения.

 

1.6. Предикат ! (отсечение)

Отсечение – встроенный предикат, который заставляет Пролог-систему забыть все указатели отката, установленные во время попыток вычислить текущую подцель (отмена поиска альтернатив для текущей подцели). Всегда выполняется успешно. Введение «!» повышает эффективность программы, сокращая время перебора и объем памяти.

Примеры:

Программа без отсечения:

data(one).

data(two).

data(three).

test(X,Y):-data(X),data(Y).

test('four','five').

?-test(X,Y).

X=one

Y=one

X=one

Y=two

X=one

Y=three

X=two

Y=one

X=two

Y=two

X=two

Y=three

X=three

Y=one

X=three

Y=two

X=three

Y=three

X=four

Y=five

Эта же программа, но с отсечением:

data(one). 

data(two).

data(three).

test(X,Y):-data(X),!,data(Y).

test('four','five').

?-test(X,Y).

X=one

Y=one

X=one

Y=two

X=one

Y=three

Другие примеры использования предиката отсечения:

f(X, 0):-X<3, !

f(X, 2):- 3=<X, X<6, !.

f(X, 4):-6 =<X, !.

minimum(X, Y, X):- X<=Y, !.

minimum(X, Y, Y):- Y<X.

add(X, L, L):-member(X, L), !.

add(X, L, [X|L]).

factorial(0, 1):-!. /* предикат

для вычисления факториала

factorial(N, RES) :- N1 is N-1,

factorial(N1, N1fak),

Res is N*N1fak.

 

ЯЗЫК ПРОГРАММИРОВАНИЯ LISP

Символы и их значения

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

Лисп оперирует символами (или именами переменных в привычном представлении, что не совсем верно), причем каждый из символов имеет значение. Таким образом, каждый из символов Лиспа – это отображение из пространства объектов в пространство имен, которым можно воспользоваться в обратную сторону, то есть «вычислить» символ.

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

Пусть помощью функции set символ а стал обозначать число 4. Если мы далее подадим на ввод Лиспа символ а, его значением в любом контексте будет 4.

Символ, значением которого является всегда он сам, называется атомом. Примерами атомов могут служить Т и NIL (логическая «истина» и логическая «ложь»), а также число 4 (как и любое другое число). Для того чтобы воспользоваться самим символом а (как реальным объектом), существует возможность блокирования вычисления символа с помощью функции quote.

 

Структуры данных

Основной и всегда используемой структурой данных Lisp является список. Список строится перечислением символов через пробел и заключением получившегося выражения в скобки, например, (А В С). Поскольку сам список, также является символом, то он может являться элементом других списков – (А (В С (D(Е F)))). В этом случае список должен представлять собой правильно построенное скобочное выражение, в котором каждой открывающей скобке соответствует закрывающая. Атомы и списки (то есть то, что раньше мы называли символами) называются еще и символьными выражениями, или s-выражениями (см. 2.2).

Внутреннее представление списка в Lisp классическое, то есть список состоит из набора связанных так называемых списочных ячеек – структуры из двух элементов значения и указателя на следующую ячейку. Последняя ячейка ссылается на пустой список (), то есть значением поля указателя является NIL. Следует отметить, что в Лиспе понятия пустого списка и булевского значения «ложь» совпадают, то есть () эквивалентно NIL.

Базовые функции

В Лиспе существует минимальный набор функций с помощью которых, вообще говоря, может быть построена любая программа (то есть более сложная функция). Рассмотрим их на примерах.

Операции над списками

(car l)

Значением аргумента l должен быть непустой список, тогда значением функции является первый элемент (верхнего уровня) этого списка.

(cdr l)

Значением аргумента l должен быть непустой список, и значением функции является "хвост" этого списка, т.е. список, полученный отбрасыванием первого элемента.

(car ’(a b с)) => a

(cdr ’(a b c)) => (b c)

(cdr ’(a)) => nil

Последний пример демонстрирует то, что последняя ячейка указывает на nil.

Кроме этих двух функций-селекторов элементов списка часто используются функции, являющиеся их суперпозициями. Имена всех таких функций начинаются на букву c, а заканчиваются на букву r, между ними же может стоять произвольная комбинация из не более чем 5 букв a и d, например,

(cadar l)≡(car(cdr(car l))) .

Предполагается, что список-аргумент l всех этих функций, так же как и следующей функции nth, содержит необходимое число элементов (в противном случае вычисления прерываются).

(car (cdr ’(a b c))) => b

(cadr ’(a b c)) => b ;; car от cdr

Функции доступа к элементам списка:

(nth n l)

Значением аргумента n должно быть положительное целое число (обозначим его N), а значением аргумента l - список. Значением функции является N-й от начала элемент этого списка.

(last l)

Функция выбирает последний (от начала) элемент списка, являющегося значением ее аргумента.

(cons e l)

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

(cons ’a ’(b с)) => (a b c)

(cons ’a ( )) => (a)

(cons (car ’(a b c)) (cdr ’(a b c)))

=> (a b c)

(append l1 l2)

Эта функция осуществляет слияние (конкатенацию) двух списков, являющихся значением двух ее аргументов.

(list e1 e2 ... en )

Еще одна функция конструктор, она имеет произвольное количество аргументов, из их значений она строит список (количество элементов результирующего списка равно количеству аргументов).

Арифметические функции

(add1 n)

Значением аргумента этой функции должно быть число, функция прибавляет к этому числу 1 и выдает результат в качестве своего значения.

(sub1 n)

Значением аргумента должно быть число, функция вычитает из него 1 и выдает результат в качестве своего значения.

(+ n1 n2)

Значениями обоих аргументов функции должны быть числа, результат вычисления функции - их сумма.

(- n1 n2)

Значениями аргументов должны быть числа, значение функции - их разность.

(length l)

Значением аргумента l должен быть список, значением функции является количество элементов (верхнего уровня) этого списка, например:

(mod n1 n2)

Значениями обоих аргументов функции должны быть целые числа. Функция выполняет деление нацело первого числа на второе, и результат выдает в качестве своего значения.

(rem n1 n2)

Значениями аргументов функции должны быть целые числа, результат вычисления функции - остаток от деления первого числа на второе.

Предикаты

Предикатом обычно называется форма (выражение), значение которой рассматривается как логическое значение "истина" или "ложь". Особенностью языка Лисп является то, что "ложью" считается пустой список, записываемый как () или nil, а "истиной" - любое другое выражение (часто в этой роли выступает атом T).

(null e)

Эта функция проверяет, является ли значение ее аргумента пустым списком: если да, то значение функции равно T, иначе – ().

(atom х)

Предикат atom возвращает истинное значение, если его аргумент является атомом, и ложь – в противном случае:

(atom ’х) => Т

(atom ’(a b c)) => NIL

(atom nil) => T

(atom ’( )) => T ;; Пустой список –

;; это nil

(atom ’(nil)) => NIL

;; (nil) - одноэлементный список

(atom (atom (+ 2 1))) => Т ;; Т - атом

Функционалы

Кроме обычных функций в Lisp существует также возможность объявления функций более высокого порядка, или функционалов. Функционал – это функция, одним из аргументов которой, в свою очередь, является функция. В различных случаях функциональный аргумент может использоваться и как данные (в том случае, если он не влияет на вычисления), и как обычная функция (в том случае, если он используется как средство, определяющее вычисления):

(car ’(lambda (x) (list x))) => lambda

;; CAR - функция,

;; лямбда-выражение - данные;

((lambda (x) (list x)) ’car) => (car)

;; CAR - данные,

;; лямбда-выражение – функция

Функционал – это функция, в которой функциональный аргумент используется в позиции и в роли функции.

Пример пояснит ситуацию. Часто используются так называемые отображающие функционалы МАРx (где x—CAR или CDR), применяющие функциональные аргументы последовательно к CAR или CDR списка и составляющие список результатов:

(mapcar ’(lambda (x) (print x))

’(1 2 3))

=> 1\n2\n3\n

(mapcdr ’(lambda (x) (print

(car x))) ’(1 2 3))

=> 2\n3\n nil\n

 

Свойства символов

В Lisp каждый символ имеет так называемые свойства. Удобно представлять символы в виде структуры из нескольких значений: непосредственно самого значения символа, а также некоторых присвоенных ему именованных значений, называемых свойствами. На самом деле свойства представляются списком, хранящимся вместе с символом (список свойств, property list, proplist), следующего вида: (имя1 значение1 имя2 значение2. . . ), например, у символа «кошка» может быть такой список свойств:

(шерсть густая цвет-шерсти белый

цвет-глаз голубой)

В Common Lisp (одна из стандартизованных реализаций языка Лисп) получение определенного свойства символа выполняется с помощью

функции get:

(get ’кошка ’цвет-шерсти) => белый

Присваивание же выполняется с помощью объединения обобщенного присваивания setf и get. Причем подобным образом может быть и заведено новое свойство:

(setf (get ’кошка ’природная-еда) ’мыши)

=> мыши

(get ’кошка ’природная-еда) => мыши

Полный список пользовательских свойств может быть получен с помощьюфункции symbolplist:

(symbol-plist ’кошка)

=> (природная-еда мыши)

Каждый символ в Lisp помимо определенных пользователем свойств содержит также список системных свойств, таких, как, например, лямбда-выражение (если с данным символом связано определение функции), тип символа и т. д. Эти свойства изменяются с помощью описанных выше функций set, defun и пр.

Таким образом, является ли данный символ функцией или нет, принадлежит ли он тому или иному типу и т. п., является свойством самого символа, что позволяет легко манипулировать этими свойствами (попробуйте в С убрать у main() свойство быть функцией).

Эта возможность Lisp позволяет заметно облегчить написание программ, управляемых данными, семантических сетей, а также объектно-ориентированных программ.

СРЕДА CLIPS

 

Простые типы данных

Для представления информации в CLIPS предусмотрено восемь простых типов данных: float, integer, symbol, string, external-address, fact-address, instance-name и instance-address. Для представления числовой информации используются типы float и integer, символьной – symbol и string. Остановимся на рассмотрении этих четырех типов данных.

При записи числа могут использоваться только цифры (0-9), десятичная точка (.), знак (+) или (–) и (е) при экспоненциальном представлении. Число сохраняется либо как целое, либо как действительное. Любое число, состоящее только из цифр, перед которыми может стоять знак, сохраняется как целое (тип integer представляется внутри CLIPS как тип языка С long integer). Все остальные числа сохраняются как действительные (float – С double float).

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

Примеры целых чисел:

237 15 +12 -32

Примеры чисел с плавающей точкой:

237е3 15.09 +12.0 -32.3е-7

Последовательность символов, которая не удовлетворяет числовым типам, обрабатывается как тип данных symbol.

Тип данных symbol в CLIPS - последовательность символов, состоящая из одного или нескольких любых печатных символов кода ASCII. Как только в последовательности символов встречается символ-разделитель, symbol заканчивается. Следующие символы служат разделителями: любой непечатный ASCII символ (включая пробел, символ табуляции, CR, LF), двойные кавычки,"(",")", "&", "|", "<","~",";". Символы-разделители не могут включаться в symbol за исключением символа "<", который может быть первым символом в symbol. Кроме того, symbol не может начинаться с символа "?" или последовательности символов "$?", поскольку эти символы зарезервированы для переменных. Заметим, что CLIPS различает регистр символов. Ниже приведены примеры выражений символьного типа:

foo Hello B76-HI bad_value

127А 742-42-42 @+=-% Search

Тип данных string - это последовательность символов, состоящая из нуля и более печатных символов и заключенная в двойные кавычки. Если внутри строки встречаются двойные кавычки, то перед ними необходимо поместить символ (\). То же справедливо и для самого (\). Несколько примеров:

"foo" "a and b" "I number" "a\"quote"

Отметим, что строка "abcd" не тоже самое, что abcd. Они содержат одина-ковые наборы символов, но являются экземплярами различного типа.

Функции

Под функцией в CLIPS понимается фрагмент исполняемого кода, с которым связано уникальное имя и который возвращает полезное значение или имеет полезный побочный эффект (например, вывод информации на экран).

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

Хотя CLIPS и не ориентирована на численные вычисления, в ней предусмотрен ряд стандартных арифметических и математических функций. Среди них:

+ Сложение

- Вычитание

* Умножение

/ Деление

* * Возведение в степень

Abs Определение абсолютного значения

Sqrt Вычисление квадратного корня

Mod Взятие по модулю

Min Нахождение минимума

Мах Нахождение максимума

Факты

Факты являются одной из основных форм представления информации в системе CLIPS. Каждый факт представляет фрагмент информации, который был помещен в текущий список фактов, называемый fact-list. Факт представляет собой основную единицу данных, используемую правилами.

Количество фактов в списке и объем информации, который может быть сохранен в факте, ограничивается только размером памяти компьютера. Если при добавлении нового факта к списку обнаруживается, что он полностью совпадает с одним из уже включенных в список фактов, то эта операция игнорируется (хотя такое поведение можно изменить).

Факт может описываться индексом или адресом. Всякий раз, когда факт добавляется (изменяется), ему присваивается уникальный целочисленный индекс. Индексы фактов начинаются с нуля и для каждого нового или измененного факта увеличиваются на единицу. Каждый раз после выполнения команд reset и clear выделение индексов начинается с нуля. Факт также может задаваться при помощи адреса. Адрес факта может быть получен путем сохранения возвращаемого значения команд, которые возвращают в качестве результата адрес факта (таких как assert, modify и duplicate), или путем связывания переменной с адресом факта в левой части правила (см. далее).

Идентификатор факта – это короткая запись для отображения факта на экране. Она состоит из символа f и записанного через тире индекса факта. Например, запись f-10 служит для обозначения факта с индексом 10. Существует два формата представления фактов: позиционный и непозиционный.

Позиционные факты состоят из выражения символьного типа, за которым следует последовательность (возможно, пустая) из полей, разделенных пробелами. Вся запись заключается в скобки. Обычно первое поле определяет "отношение", которое применяется к оставшимся полям. Например: (the pump is on) (altitude is 10000 feet) (grocery_list bread milk eggs)

Поля в позиционных фактах могут быть любого простого типа (за исключением первого поля, которое всегда должно быть типа symbol), на порядок полей также не накладывается никаких ограничений. Следующие символьные выражения зарезервированы и не должны использоваться как первое поле любого факта (позиционного или нет): test, and, or, not, declare, logical, object, exists u forall.

Для того чтобы обратиться к информации, содержащейся в позиционном факте, пользователь должен знать не только какие данные содержатся в факте, но и то, в каком поле они хранятся. Непозиционные (шаблонные) факты дают возможность пользователю абстрагироваться от структуры факта, задавая имена каждому из полей факта. Для задания шаблона, который затем может использоваться при доступе к полям по именам, используется конструкция deftemplate. Эта конструкция подобна структуре или записи в языках программирования С и Паскаль.

Конструкция deftemplate позволяет наряду с определением именованных полей, или слотов, вводить имя шаблона. В отличие от позиционных фактов слоты шаблонного факта могут быть ограничены по типу, значению, числовому диапазону. Кроме того, для любого слота можно определить значения по умолчанию. Слот состоит из открывающейся скобки, за которой следует имя слота, полей (могут отсутствовать) и закрывающейся скобки. Заметим, что слоты не могут использоваться в позиционных фактах, так же как позиционные поля не могут использоваться в шаблонных фактах. Общая структура конструкции deftemplate такова:

(deftemplate )

(slot-1)

(slot-2)

(slot-N)

Далее приведен пример шаблона с заданными для слотов значениями по умолчанию:

(deftemplate prospect)

(slot name

(default ?DERIVE)

(slot. assets

(default rich)

(slot age

(default 80 )))

Шаблонные факты отличаются от позиционных по первому полю в факте. Первое поле всех фактов должно быть типа symbol, но если это символьное выражение соответствует имени шаблона, то этот факт - шаблонный. За первым полем шаблонного факта следует список из нуля или более слотов. Как и позиционные, шаблонные факты заключаются в скобки. Далее приведено несколько примеров шаблонных фактов:

(client (name "Joe Brown") (id X9345A))

(point-mass (x-velocity 100)

(y-velocity -200))

(class (teacher "Martha Jones")

(#-students 30)

(room "37A"))

(grocery-list (#-of-items 3)

(items bread milk eggs))

Заметим, что порядок следования слотов в шаблонном факте не важен.

Манипуляции над фактами

Факты могут добавляться к списку фактов (с помощью команды assert), удаляться из него (с помощью команды retract), изменяться (с помощью команды modify) и дублироваться (с помощью команды duplicate) самим пользователем или программой. Например:

CLIPS> (assert (today is Sunday))

CLIPS> (assert (weather is warm))

(assert (light green))

Для вывода списка фактов, имеющихся в базе, используется команда facts:

CLIPS>(facts)

f-1 (today is Sunday)

f-2 (weather is warm)

В последних версиях CLIPS , в частности, в той, которая работает в операционной среде Windows, такие команды как facts, можно вызвать с помощью меню.

Кроме того, конструкция deffacts позволяет определить множество исходных или априорных знаний в виде набора фактов. Например:

(deffacts walk "Some facts about walking"

(status walking)

(walk-sign walk) )

Для удаления фактов из базы используется команда retract.

CLIPS> (retract 1)

CLIPS> (facts)

f-0 (today is Sunday)

Эти же команды, assert и retract, используются в выполняемой части правил (заключении правила) и с их помощью выполняется программное изменение базы фактов. Часто приходится пользоваться и другой командой интерпретатора, clear, которая очищает базу фактов (как правило, эта команда доступна в одном из выпадающих меню).

CLIPS> (clear)

CLIPS> (facts)

В тексте программы факты можно включать в базу не по одиночке, а целым массивом. Для этого в CLIPS имеется команда deffacts.

(deffacts today

(today is Sunday)

(weather is warm)

)

Выражение deffacts имеет формат, аналогичный выражениям в языке LISP. Выражение начинается с команды deffacts, затем приводится имя списка фактов, который программист собирается определить (в нашем примере – today), а за ним следуют элементы списка, причем их количество не ограничивается. Этот массив фактов можно затем удалить из базы командой undeffacts.

CLIPS> (undeffacts today)

Выражение deffacts можно вводить и в командную строку нтерпретатора, но лучше записать его в текстовый файл с помощью редактора CLIPS или любого другого текстового редактора. Загрузить этот файл в дальнейшем можно с помощью команды в меню File либо из командной строки.

CLIPS> (load “my file”)

Однако после загрузки файла факты не передаются сразу же в базу фактов CLIPS. Команда deffacts просто указывает интерпретатору, что существует массив today, который содержит множество фактов. Собственно загрузка выполняется командой reset.

CLIPS> (reset)

Когда производится сброс состояния среды CLIPS (с помощью команды reset) все факты, описанные в конструкции deffacts, добавляются к списку фактов. Кроме того, по этой команде в список фактов заносится исходный факт (initial-fact). Этот факт включается в список фактов всегда с идентификатором f-0. Его назначение будет рассмотрено в следующем пункте.

 

Правила

Одним из основных методов представления знаний в CLIPS являются правила. Правила используются для представления эвристик, определяющих ряд действий, которые необходимо выполнить в определенной ситуации. Разработчик экспертной системы определяет совокупность правил, которые используются совместно для решения проблемы. Правило состоит из двух частей: антецедента (условия), который является аналогом условия в if-then операторе и записывается слева, и консеквента (заключения), который является аналогом then части этого оператора и записывается справа.

Левая часть правила представляет собой ряд условий (условных элементов), которые должны выполняться, чтобы правило было применимо. В CLIPS принято считать, что условие выполняется, если соответствующий ему факт присутствует в списке фактов. Одним из типов условных элементов может быть образец. Образцы состоят из набора ограничений, которые используются для описания того, какие факты удовлетворяют условию, определяемому образцом. Процесс сопоставления фактов и образцов выполняется блоком вывода CLIPS, который автоматически сопоставляет образцы, исходя из текущего состояния списка фактов, и определяет, какие из правил являются применимыми. Если все условия правила выполняются, то оно активируется и помещается в список активированных правил.

Если левая часть правила пуста, то для его активации необходимо наличие в списке фактов исходного факта (initial-fact). Такие безусловные правила часто используются для того, чтобы инициировать работу программы. Поэтому перед запуском таких программ необходимо произвести сброс состояния среды CLIPS.

Правая часть правила представляет собой совокупность действий, которые должны быть выполнены, если правило применимо. Действия, описанные в применимых правилах, выполняются тогда, когда блок вывода CLIPS получает команду начать выполнение применимых правил. Если существует множество применимых правил, то для того, чтобы выбрать правило, действия которого должны быть выполнены, блок вывода использует стратегию разрешения конфликтов. Действия, описанные в выбранном правиле, выполняются (при этом список применимых правил может измениться), а затем блок вывода выбирает другое правило и т.д. Этот процесс продолжается до тех пор, пока не остается ни одного применимого правила, т.е. пока список активированных правил не окажется пуст.

Во многом правила похожи на операторы типа if-then процедурных языков программирования. Однако условие if-then оператора в процедурном языке проверяется только тогда, когда программа передает ему управление. С правилами ситуация иная. Блок вывода постоянно отслеживает все правила, условия которых выполняются, и, таким образом, правило может быть выполнено в любой момент, как только оно становится применимым. В этом смысле правила подобны обработчикам исключительных ситуаций в процедурных языках (например, в языке Ада).

Для определения правил используется конструкция defrule:

(defrule <имя правила>

< необязательный комментарий >

< необязательное объявление >

< предпосылка_1 >

……………….

< предпосылка_m >

=>

< действие_1 >

………………..

< предпосылка_n >

)

Например:

(defrule chores

“Things to do on Sunday”

(salience 10 )

(today is Sunday)

(weather is warm)

=>

(assert (wash car))

(assert (chop wood)

)

В этом примере c hores – произвольно выбранное имя правила. Предпосылки в условной части правила

(today is Sunday)

(weather is warm)

сопоставляются затем интерпретатором с базой фактов, а действия, перечисленные в выполняемой части правила (она начинается после пары символов =>), вставят в базу два факта

(wash car)

(chop wood)

в случае, если правило будет активизировано. Приведенный в тексте правила комментарий

“Things to do on Sunday”

“Что делать в воскресенье”

поможет в дальнейшем вспомнить, чего ради это правило включено в программу. Выражение

(salience 10)

указывает на степень важности правила. Пусть например, в программе имеется другое правило

(defrule fun

“Better things to do on Sunday”

(salience 100)

(today is Sunday)

(weather is warm)

=>

(assert (drink beer))

(assert (play guitar))

)

Поскольку предпосылки обоих правил одинаковы, то при выполнении оговоренных условий они будут «конкурировать» за внимание интерпретатора. Предпочтение будет отдано правилу, у которого параметр salience имеет более высокое значение, в данном случае – правилу fun. Параметру salience может быть присвоено любое целочисленное значение в диапазоне [-10000, 10000]. Если параметр salience в определении правила опущен, ему по умолчанию присваивается значение 0.

 

Переменные

Как и в других языках программирования, в CLIPS для хранения значений используются переменные. В отличие от фактов, которые являются статическими, или неизменными, содержание переменной динамично и изменяется по мере того, как изменяется присвоенное ей значение. Идентификатор переменной всегда начинается с вопросительного знака, за которым следует ее имя. В общем случае формат переменной выглядит следующим образом:

?имя_переменной

Примеры переменных:

?х ?sensor ?noun ?color

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

(defrule make-quack

(duck-sound ?sound)

=>

(assert (sounds-is ?sound) )

Получив значение, переменная сохраняет его неизменным при использовании как в левой, так и в правой части правила, если только это значение не изменяется в правой части при помощи функции bind.

(defrule addition

(numbers ?x ?y)

=>

(assert (answer (+ ?x ?y)))

(bind ?answer (+ ?x ?y))

(printout t "answer is " ?answer crlf))

Другой пример:

(defrule pick-a-chore

“Allocating chores to days”

(today is ?day)

(chore is ?job)

=>

(assert (do ?job on ?day))

)

будет сопоставлено с фактами

(today is Sunday)

(chore is carwash)

то в случае активизации оно включит в базу новый факт

(do carwash on Sunday).

Аналогично, правило

(defrule drop-a-chore

“Allocating chores to days”

(today is ?day)

?chore <- (do ?job on ?day)

=>

(retract ?chore)

)

отменит выполнение работ по дому (a chore). Обратите внимание на то, что оба экземпляра переменной ?day должны получить одно и то же значение. Переменная ?chore в результате сопоставления должна получить ссылку на факт, который мы собираемся исключить из базы. Таким образом, если это правило будет сопоставлено с базой фактов, в которой содержатся

(today is Sunday)

(do carwash on Sunday)

то при активизации правила из базы будет удален факт

(do carwash on Sunday)

С подробностями выполнения процесса сопоставления в интерпретаторе CLIPS вы сможете познакомиться в Руководстве пользователя , а здесь только отметим, что факт

(do carwash on Sunday)

будет сопоставлен с любым из представленных ниже образцов

(do ? ? Sunday)

(do ? on ?)

(do ? on ?when)

(do $?)

(do $? Sunday)

(do ?chore $?when)

Учтите, что префикс $? является признаком сегментной переменной, которая будет связана с сегментом списка. Например, в приведенном выше примере переменная $?when будет связана с (on Sunday)

Если за префиксами ? и $? не следует имя переменой, они рассматриваются как универсальные символы подстановки, которым соответственно может быть сопоставлен любой элемент или сегмент списка.

Кроме значения самого факта, переменной может быть присвоено значение адреса факта. Это может оказаться удобным при необходимости манипулировать фактами непосредственно из правила. Для такого присвоения используется комбинация "<-". Следующий пример иллюстрирует присвоение переменной значения адреса факта и ее последующее использование:

(defrule get-married

?duck <- (bachelor Dopey)

=>

(retract ?duck))

Для определения глобальных переменных, которые видны всюду в среде CLIPS, используется конструкция defglobal. К глобальной переменной можно обратиться в любом месте, и ее значение остается независимым от других конструкций. Глобальные переменные CLIPS подобны глобальным переменным в процедурных языках программирования, но они значительно слабее типизированы (на них не налагается ограничения хранения данных только одного типа).

Использование шаблонов

Для определения фактов можно использовать не только списочные структуры, но и шаблоны, которые напоминают простые записи. (Шаблоны в CLIPS не имеют ничего общего с шаблонами C++.) Шаблон выглядит примерно так:

(deftemplate student “cmt”

(slot name (type STRING))

(slot age (type NUMBER) (default 18))

)

Каждое определение шаблона состоит из произвольного имени шаблона, необязательного комментария и некоторого количества определений слотов. Слот включает поле данных, например name, и тип данных, например STRING. Можно указать и значение по умолчанию, как в приведенном выше примере.Если в программу включено приведенное выше определение шаблона, то выражение

(deffacts students

(student (name fred))

(student (name freda) (age 19))

)

приведет к тому, что в базу фактов после выполнения команды reset будет добавлено

(student (name fred) (age 18))

(student (name freda) (age 19))

Библиографический список

 

1. Адаменко, А.Н. Логическое программирование и Visual Prolog / А.Н.Адаменко, А.М.Кучуков. СПб.: БХВ-Петербург, 2003. 992 с.

2. Братко, Иван. Алгоритмы искусственного интеллекта на языке PROLOG, 3-е издание. : Пер. с англ. / Иван Братко. М.: Издательский дом «Вильямс», 2004. 640 с.

3. Люгер, Джордж, Ф. Искусственный интеллект: стратегии и методы решения сложных проблем, 4-е издание. : Пер. с англ. / Джордж Ф. Люгер. М.: Издательский дом «Вильямс», 2003. 864 с.

4. Городняя, Л.В. Основы функционального программирования: курс лекций. учеб. пос. / Л.В. Городняя. М.: ИНТУИТюРУ «Интернет-университет Информационных технологий», 2004. 280 с.

5. Хьюванен, Э. Мир Лиспа. Том 1. Введение в язык Лисп и функциональное программирование. : Пер. с финск. / Э. Хьюванен, Й. Сеппянен. М.: Мир, 1990. 447 с.

6. Хьюванен, Э. Мир Лиспа. Том 2. Методы и системы программирования. : Пер. с финск. / Э. Хьюванен, Й. Сеппянен. М.: Мир, 1990. 332 с.

7. Частиков, А.П. Разработка экспертных систем. Среда CLIPS. / А.П. Частиков, Т.А. Гаврилова, Д.Л. Белов. СПб.: БХВ-Петербург, 2003. 608 с.


 

ОГЛАВЛЕНИЕ

ВВЕДЕНИЕ. 3

1. ЯЗЫК ПРОГРАММИРОВАНИЯ PROLOG.. 5

1.1. Основы языка Пролог. 5

1.2. Типы предложений в языке PROLOG.. 6

1.3. Унификация переменных. 9

1.4. Списки. 12

1.5. Откат (backtrace) 14

1.6. Предикат ! (отсечение) 14

1.7. Представление данных структурами. 16

1.8. Обновление базы данных Пролога. 17

2. ЯЗЫК ПРОГРАММИРОВАНИЯ LISP. 18

2.1. История создания ЯП LISP. Парадигма функционального программирования. 19

2.2. Основные особенности ЯП LISP. 20

2.3. Символы и их значения. 21

2.4. Структуры данных. 22

2.5. Функции. Запись функций и программ. 22

2.6. Функции с побочным эффектом. 24

2.7. Базовые функции. 25

2.8. Предикаты.. 27

2.9. Определения функций. Формализм их задания. 30

2.10. Функционалы.. 32

2.11. Свойства символов. 32

2.12. Основные недостатки языка Лисп. 34

3. СРЕДА CLIPS. 35

3.1. Общие сведения и краткая история среды CLIPS. 35

3.2. Простые типы данных. 36

3.3. Функции. 38

3.4. Конструкции. Определение функций непосредственно в среде CLIPS 39

3.5. Факты.. 41

3.6. Манипуляции над фактами. 43

3.7. Правила. 45

3.8. Переменные. 49

3.9. Использование шаблонов. 52

3.10. Объектно-ориентированные средства в CLIPS. 52

3.11. Стиль программирования на языке CLIPS. 61

Библиографический список. 62

 

 


А.Н. ГУЩИН

 

ЯЗЫКОВЫЕ СРЕДСТВА РАЗРАБОТКИ

ИНТЕЛЛЕКТУАЛЬНЫХ СИСТЕМ

 

 

Учебное пособие

 

Санкт-Петербург

2006

 


 

 

Гущин, А.Н.

Языковые средства разработки интеллектуальных систем: учеб. пособие / А.Н.Гущин; Балт. гос. техн. ун-т. – СПб., 2006. – с.

 

Учебное пособие содержит основные сведения о языках программирования PROLOG, LISP и CLIPS, применяемых для разработки интеллектуальных систем. Имеются примеры применения синтаксических конструкций рассматриваемых языков.

Предназначено для студентов и магистрантов, обучающихся по специальностям «Автоматизированные системы обработки информации и управления» и «Информационные системы и технологии»




ВВЕДЕНИЕ

 

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

Для разработки интеллектуальных систем могут применятся различные языковые средства и соответствующие им инструментальные средства разработки. Учитывая многообразие форм построения и областей применения современных интеллектуальных систем, в качестве языковых средств разработки могут применяться как традиционные языки программирования достаточно низкого уровня, такие как С, Pascal, Java или Basic, так и специализированные языки разработки интеллектуальных систем.

Языки программирования интеллектуальных систем – языки программирования, ориентированные на разработку интеллектуальных информационных систем, как правило имеющие парадигму, отличную от традиционных для языков программирования общего назначения процедурных парадигм.

Процедурные (императивные) парадигмы (процедурный подход) – как сделать?

Декларативные парадигмы (декларативный подход) – что мы знаем? что хотим узнать?

Промежуточное положение между процедурным и декларативным подходом занимает объектно-ориентированный подход (ООП): при рассмотрении объектов в программе, как моделей объектов реального мира – ООП можно отнести к декларативному подходу, при рассмотрении реализации поведения объектов – ООП можно отнести к императивному подходу.

К языкам программирования интеллектуальных систем можно отнести:

– LISP

– PROLOG

– CLIPS

– Haskell

– и многие другие

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

Поскольку полностью охватить весь спектр языков разработки интеллектуальных систем в рамках данного пособия не представляется возможным, рассмотрим (на самом элементарном уровне) по одному представителю трех семейств: из языков логического программирования – ЯП PROLOG, из языков функционального программирования – ЯП LISP, из специализированных языков разработки экспертных систем – язык системы CLIPS.

 

Предлагаемое учебное пособие состоит из трех разделов. Первый раздел посвящен рассмотрению принципов построения программ на ЯП PROLOG. Во втором разделе приведены базовые сведения о ЯП LISP и его использовании при разработки интеллектуальных систем. Третий раздел освещает основные возможности ЯП CLIPS, применяемого в одноименной среде разработки экспертных систем.

 


 


ЯЗЫК ПРОГРАММИРОВАНИЯ PROLOG

Основы языка Пролог

Язык программирования Пролог (PROgramming LOGic – программирование в терминах логики) предполагает получение решения задачи при помощи логического вывода из ранее известных фактов и правил. Пролог был создан в 1971 г. на факультете естественных наук в Марселе.

Программа на языке Пролог не является последовательностью действий – она состоит из описания задачи (набор фактов и правил), а Пролог-система сама строит логический вывод на основе введённых данных. В связи с этим Пролог считается декларативным языком программирования.

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

константы;

переменные;

предикаты, структуры (составные термы или функции), состоящие из имени предиката и списка аргументов-термов; имена предикатов начинаются со строчной буквы.

Константы - это поименованные конкретные объекты или отношения. Константа начинается со строчной буквы, либо заключаются в одинарные кавычки. Также константа может быть числом.

Переменные служат для обозначения объектов, значения которых меняются в ходе выполнения программы. Имена переменных начинаются с заглавных букв или знака «_» Область действия переменной – предложение. Одноименные переменные в разных предложениях могут иметь разные значения.

Специальным знаком «_» обозначается анонимная переменная, которая используется тогда, когда конкретное значение переменной не существенно для данного предложения. Значение анонимной переменной не выводится на печать.

Предикат – это единый объект состоящий из совокупности других объектов, называемых компонентами. Компоненты в свою очередь могут быть также предикатами. Название предиката стоит перед скобками, а компоненты внутри скобок перечисляются через запятую. Название предиката – функтор.

владелец (иван, книга(война и мир)).

функтор различается двумя параметрами: именем и числом параметров

point( X, Y, Z) и point(X, Y) – разные предикаты

point/3 – это означается, что у предиката point 3 аргумента,

point/2 – это означается, что у предиката point 2 аргумента.

Комментарии

Комментарии в программе помещаются между /* и */ (для любого количества строк), либо после символа % на той же строчке.


Поделиться:



Последнее изменение этой страницы: 2019-06-20; Просмотров: 263; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.455 с.)
Главная | Случайная страница | Обратная связь