А.3. Объектно-ориентированные средства в CLIPS
А.3. Объектно-ориентированные средства в CLIPS
Использование объектно-ориентированных средств в CLIPS позволяет значительно упростить программирование правил, поскольку для обновления данных можно применять механизм передачи и обработки сообщений методами классов. В этом разделе мы продемонстрируем, как это делается на примере, который моделирует правила обращения с полуавтоматическим пистолетом.
Первым делом определим класс pistol, в котором будут перечислены свойства, необходимые для моделирования.
(defclass pistol
(is-a USER)
(role concrete)
(pattern-match reactive)
(slot safety (type SYMBOL) (create-accessor read-write))
(slot slide (type SYMBOL) (create-accessor read-write))
(slot hammer (type SYMBOL) (create-accessor read-write))
(slot chamber (type INTEGER) (create-accessor read-write))
(slot magazine (type SYMBOL) (create-accessor read-write))
(slot rounds (type INTEGER) (create-accessor read-write)) )
- pistol — это пользовательский класс;
- pistol является конкретным классом, т.е. возможно создание экземпляров этого класса (альтернативный тип — абстрактный класс, который играет ту же роль, что и виртуальный класс в C++);
- экземпляры класса pistol могут быть использованы в качестве объектов данных, которые можно сопоставлять с условиями в правилах и использовать в действиях, определенных правилами.
- слот safety (предохранитель) может содержать символ on или off;
- слот slide (затвор) может содержать значение forward или back, т.е. хранит информацию о положении затвора;
- слот hammer (курок) содержит информацию о состоянии курка, back или down;
- слот chamber (патронник) содержит значение 1 или 0, в зависимости от того, есть ли патрон в патроннике;
- слот magazine (обойма) может содержать значение in или out, в зависимости от того, вставлена ли обойма;
- слот rounds (патроны) содержит текущее количество патронов в обойме.
(definstances pistols (РРК of pistol (safety on)
(slide forward) (hammer down) (chamber 0) (magazine out) (rounds 6)
Теперь, имея в программе определение класса и сформировав экземпляр класса, разработаем правила и обработчики сообщений, с помощью которых можно описать отдельные операции обращения с пистолетом и стрельбы из него. Для этого сначала разработаем шаблон задачи. Желательно отслеживать две вещи:
- есть ли патрон в патроннике;
- произведен ли выстрел.
(deftemplate range-test
(field check (type SYMBOL) (default no))
(field fired (type SYMBOL) (default no)) )
(defrule start
(initial-fact) =>
(assert (range-test)) )
Следующие три правила будут проверять, правильно ли снаряжен пистолет.
(defrule check
(object (name [PPK]) (safety on) (magazine out)
?T <- (range-test (check no)) =>
(send [PPK] clear)
(modify ?T (check yes) )
(defmessage-handler pistol clear ( )
(dynamic-put chamber 0)
(ppinstance) )
В следующих двух правилах обрабатываются ситуации, когда пистолет снаряжен неправильно, — не установлен на предохранитель или в него вставлена обойма. Правило correctl устанавливает пистолет на предохранитель, а правило correct2 извлекает из него обойму.
(defrule correctl
(object (name [PPK]) (safety off) )
(range-test (check no)) =>
(send [PPK] safety on)
)
(defrule correct2
(object (name [PPK]) (safety on) (magazine in))
(range-test (check no)) =>
(send [PPK] drop) )
(defmessage-handler pistol safety (?on-off)
(dynamic-put safety ?on-off)
(if (eq ?on-off on)
then (dynamic-put hammer down)
) )
Обработчик сообщения drop просто извлекает обойму из пистолета.
(defmessage-handler pistol drop ()
(dynamic-put magazine out) )
(defrule mag-in
(object (name [PPK]) (safety on) (magazine out))
(range-test (fired no) (check yes)) =>
(send [PPK] seat) )
(defmessage-handler pistol seat ()
(dynamic-put magazine in) )
(defrule mag-in
?gun <- (object (name [PPK]) (safety on)
(magazine out)) (range-test (fired no) (check yes)) =>
(modify ?gun (magazine in) )
(defrule load
(object (name [PPK]) (magazine in) (chamber 0)) =>
(send [PPK] rack) )
(defmessage-handler pistol rack ()
(if (> (dynamic-get rounds) 0) then (dynamic-put chamber 1)
(dynamic-put rounds (- (dynamic-get rounds) 1))
(dynamic-put slide forward) else (dynamic-put chamber 0)
(dynamic-put slide back)
(defrule ready
(object (name [PPK]) (chamber 1)) =>
(send [PPK] safety off) )
Правило fire выполняет стрельбу.
(defrule fire
(object (name [PPK]) (safety off);
?T <- (range-test (fired no)) =>
(if (eq (send [PPK] fire) TRUE)
then (modify ?T (fired yes))) )
(def message-handler pistol fire () (if (and
(eq (dynamic-get chamber) 1) (eq (dynamic-get safety) off)
)
then (printout t crlf "BANG!" t crlf)
TRUE
else (printout t crlf "click" t crlf) FALSE
)
После завершения стрельбы пистолет нужно вновь вернуть в положение "по-походному". Начинается это с того, что пистолет устанавливается на предохранитель, для чего используется ранее разработанный обработчик сообщения safety.
(defrule unready
(object (name [PPK]) (safety off))
(range-test (fired yes)) =>
(send [PPK] safety on) )
(defrule drop
(object (name [PPK]) (safety on))
(range-test (fired yes)) =>
(send [PPK] drop) )
(defrule unload
(object (name [PPK]) (safety on) (magazine out))
(range-test (fired yes)) =>
(send [PPK] clear) )