Планирование пролога с использованием retract и assert - PullRequest
1 голос
/ 04 апреля 2019

Интересно, возможно ли планирование в Прологе с использованием базы знаний, измененной с помощью retract и assert во время выполнения?

Моя идея заключается в следующем: предположим, что мне нужно заменить спущенную шинуавтомобиль.Я могу либо положить что-то на землю, либо перенести что-нибудь с земли в какое-нибудь свободное место.

Поэтому я придумал такой код:

at(flat, axle).
at(spare, trunk).

free(Where) :- at(_, Where), !, fail.
remove(What) :- at(What, _), retract(at(What, _)), assert(at(What, ground)).
put_on(What, Where) :- at(What, _), free(Where), retract(at(What, _)), assert(at(What, Where)).

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

Идея такова: у меня спущенная шина на оси и запасная в багажнике.Я могу удалить вещь X, если X где-то есть, и удалить ее, я убираю факт, указывающий, где она находится, и добавляю факт, что она на земле.Точно так же я могу поместить вещь X в местоположение Y, если X где-то и Y свободен, и для этого я удаляю X из того места, где она есть, и добавляю факт, что X находится в Y.

И теперь яя застрял: я понятия не имею, как использовать этот код сейчас, так как at(spare, axle) просто говорит нет, даже с трассировкой.

Итак, вопрос: можно ли использовать такой подход, и если да, то как?

Надеюсь, это имеет смысл.

1 Ответ

1 голос
/ 04 апреля 2019

Использование примера кода из «Искусственного интеллекта - структуры и стратегии комплексного решения проблем» Джорджа Ф. Люгера ( WorldCat )

ADTS

%%%
%%% This is one of the example programs from the textbook:
%%%
%%% Artificial Intelligence: 
%%% Structures and strategies for complex problem solving
%%%
%%% by George F. Luger and William A. Stubblefield
%%% 
%%% Corrections by Christopher E. Davis (chris2d@cs.unm.edu)
%%%
%%% These programs are copyrighted by Benjamin/Cummings Publishers.
%%%
%%% We offer them for use, free of charge, for educational purposes only.
%%%
%%% Disclaimer: These programs are provided with no warranty whatsoever as to
%%% their correctness, reliability, or any other property.  We have written 
%%% them for specific educational purposes, and have made no effort
%%% to produce commercial quality computer programs.  Please do not expect 
%%% more of them then we have intended.
%%%
%%% This code has been tested with SWI-Prolog (Multi-threaded, Version 5.2.13)
%%% and appears to function as intended.

%%%%%%%%%%%%%%%%%%%% stack operations %%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    % These predicates give a simple, list based implementation of stacks

    % empty stack generates/tests an empty stack

member(X,[X|_]).
member(X,[_|T]):-member(X,T).

empty_stack([]).

    % member_stack tests if an element is a member of a stack

member_stack(E, S) :- member(E, S).

    % stack performs the push, pop and peek operations
    % to push an element onto the stack
        % ?- stack(a, [b,c,d], S).
    %    S = [a,b,c,d]
    % To pop an element from the stack
    % ?- stack(Top, Rest, [a,b,c]).
    %    Top = a, Rest = [b,c]
    % To peek at the top element on the stack
    % ?- stack(Top, _, [a,b,c]).
    %    Top = a 

stack(E, S, [E|S]).

%%%%%%%%%%%%%%%%%%%% queue operations %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    % These predicates give a simple, list based implementation of 
    % FIFO queues

    % empty queue generates/tests an empty queue


empty_queue([]).

    % member_queue tests if an element is a member of a queue

member_queue(E, S) :- member(E, S).

    % add_to_queue adds a new element to the back of the queue

add_to_queue(E, [], [E]).
add_to_queue(E, [H|T], [H|Tnew]) :- add_to_queue(E, T, Tnew).

    % remove_from_queue removes the next element from the queue
    % Note that it can also be used to examine that element 
    % without removing it

remove_from_queue(E, [E|T], T).

append_queue(First, Second, Concatenation) :- 
    append(First, Second, Concatenation).

%%%%%%%%%%%%%%%%%%%% set operations %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    % These predicates give a simple, 
    % list based implementation of sets

    % empty_set tests/generates an empty set.

empty_set([]).

member_set(E, S) :- member(E, S).

    % add_to_set adds a new member to a set, allowing each element
    % to appear only once

add_to_set(X, S, S) :- member(X, S), !.
add_to_set(X, S, [X|S]).

remove_from_set(_, [], []).
remove_from_set(E, [E|T], T) :- !.
remove_from_set(E, [H|T], [H|T_new]) :-
    remove_from_set(E, T, T_new), !.

union([], S, S).
union([H|T], S, S_new) :- 
    union(T, S, S2),
    add_to_set(H, S2, S_new).   

intersection([], _, []).
intersection([H|T], S, [H|S_new]) :-
    member_set(H, S),
    intersection(T, S, S_new),!.
intersection([_|T], S, S_new) :-
    intersection(T, S, S_new),!.

set_diff([], _, []).
set_diff([H|T], S, T_new) :- 
    member_set(H, S), 
    set_diff(T, S, T_new),!.
set_diff([H|T], S, [H|T_new]) :- 
    set_diff(T, S, T_new), !.

subset([], _).
subset([H|T], S) :- 
    member_set(H, S), 
    subset(T, S).

equal_set(S1, S2) :- 
    subset(S1, S2), subset(S2, S1).

%%%%%%%%%%%%%%%%%%%%%%% priority queue operations %%%%%%%%%%%%%%%%%%%

    % These predicates provide a simple list based implementation
    % of a priority queue.

    % They assume a definition of precedes for the objects being handled

empty_sort_queue([]).

member_sort_queue(E, S) :- member(E, S).

insert_sort_queue(State, [], [State]).  
insert_sort_queue(State, [H | T], [State, H | T]) :- 
    precedes(State, H).
insert_sort_queue(State, [H|T], [H | T_new]) :- 
    insert_sort_queue(State, T, T_new). 

remove_sort_queue(First, [First|Rest], Rest).

Планировщик

%%%%%%%%% Simple Prolog Planner %%%%%%%%
%%%
%%% This is one of the example programs from the textbook:
%%%
%%% Artificial Intelligence: 
%%% Structures and strategies for complex problem solving
%%%
%%% by George F. Luger and William A. Stubblefield
%%% 
%%% Corrections by Christopher E. Davis (chris2d@cs.unm.edu)
%%%
%%% These programs are copyrighted by Benjamin/Cummings Publishers.
%%%
%%% We offer them for use, free of charge, for educational purposes only.
%%%
%%% Disclaimer: These programs are provided with no warranty whatsoever as to
%%% their correctness, reliability, or any other property.  We have written 
%%% them for specific educational purposes, and have made no effort
%%% to produce commercial quality computer programs.  Please do not expect 
%%% more of them then we have intended.
%%%
%%% This code has been tested with SWI-Prolog (Multi-threaded, Version 5.2.13)
%%% and appears to function as intended.

:- [adts].
plan(State, Goal, _, Moves) :-  equal_set(State, Goal), 
                write('moves are'), nl,
                reverse_print_stack(Moves).
plan(State, Goal, Been_list, Moves) :-  
                move(Name, Preconditions, Actions),
                conditions_met(Preconditions, State),
                change_state(State, Actions, Child_state),
                not(member_state(Child_state, Been_list)),
                stack(Child_state, Been_list, New_been_list),
                stack(Name, Moves, New_moves),
            plan(Child_state, Goal, New_been_list, New_moves),!.

change_state(S, [], S).
change_state(S, [add(P)|T], S_new) :-   change_state(S, T, S2),
                    add_to_set(P, S2, S_new), !.
change_state(S, [del(P)|T], S_new) :-   change_state(S, T, S2),
                    remove_from_set(P, S2, S_new), !.
conditions_met(P, S) :- subset(P, S).


member_state(S, [H|_]) :-   equal_set(S, H).
member_state(S, [_|T]) :-   member_state(S, T).

reverse_print_stack(S) :-   empty_stack(S).
reverse_print_stack(S) :-   stack(E, Rest, S), 
                reverse_print_stack(Rest),
                write(E), nl.


/* sample moves */

move(pickup(X), [handempty, clear(X), on(X, Y)], 
        [del(handempty), del(clear(X)), del(on(X, Y)), 
                 add(clear(Y)), add(holding(X))]).

move(pickup(X), [handempty, clear(X), ontable(X)], 
        [del(handempty), del(clear(X)), del(ontable(X)), 
                 add(holding(X))]).

move(putdown(X), [holding(X)], 
        [del(holding(X)), add(ontable(X)), add(clear(X)), 
                  add(handempty)]).

move(stack(X, Y), [holding(X), clear(Y)], 
        [del(holding(X)), del(clear(Y)), add(handempty), add(on(X, Y)),
                  add(clear(X))]).

go(S, G) :- plan(S, G, [S], []).
test :- go([handempty, ontable(b), ontable(c), on(a, b), clear(c), clear(a)],
              [handempty, ontable(c), on(a,b), on(b, c), clear(a)]).

Большая часть кода остается прежней, единственными изменениями, необходимыми для решения вашего вопроса, являются предикаты move/3 и запрос test. Закомментируйте или удалите предикаты move/3 и test/0 из приведенного выше кода, прежде чем добавлять предикаты для решения вашего вопроса.

Ниже приведены все новые необходимые предикаты, move/3 и test/0. Первый move/3 показан, а остальные должны быть обнаружены (наведите курсор мыши на желтое поле ниже), чтобы вы могли видеть их при необходимости, но вы должны попытаться сделать их самостоятельно.

move(take_from_trunk(X), [hand(empty), trunk(X)],
    [del(hand(empty)), del(trunk(X)),
        add(hand(X)), add(trunk(empty))]).

Состояние отслеживает четыре местоположения hand, ground, axle и trunk и три значения flat, spare и empty для местоположений. Предикат move/3 также использует переменные, чтобы они не фиксировались в том, что они могут делать.

Предикат move/3 имеет 3 параметра.
1. Имя: что появляется в ответе, например, take_from_trunk(spare).
2. Предварительные условия: условия, которые должны присутствовать в state для применения перемещения.
3. Действия: изменения, сделанные, чтобы указать, применен ли ход. Они заменят ваши assert и retract. Изменения очень просты, вы удаляете некоторые свойства состояния, например, del(hand(empty)) и добавьте, например, add(hand(X)). Для данной проблемы это решение простое в том, что для каждого изменения для каждого del есть соответствующий add.

Запрос:

test :- go([hand(empty), trunk(spare), axle(flat), ground(empty)],
            [hand(empty), trunk(flat), axle(spare), ground(empty)]).

Пример выполнения:

?- test.
moves are
take_from_trunk(spare)
place_on_ground(spare)
take_off_axle(flat)
place_in_trunk(flat)
pickup_from_ground(spare)
place_on_axle(spare)
true.

Другие move/3 предикаты необходимы. Попробуйте сделать это самостоятельно.

При написании этих предикатов некоторые из move/3 не работали, как я ожидал, поэтому я создал простые тестовые запросы для каждого, чтобы проверить их.

Использование теста также помогло мне изменить то, что было в state и как оно было представлено, например, вместо handempty и holding(X) оно было изменено на hand(empty) и hand(X), что было легче понять , следуйте и проверяйте целостность кода, но, скорее всего, сделайте код более неэффективным.

test_01 :- go([hand(empty), trunk(spare), axle(flat), ground(empty)],
            [hand(spare), trunk(empty), axle(flat), ground(empty)]).

test_02 :- go([hand(empty), trunk(spare), axle(flat), ground(empty)],
            [hand(flat), trunk(spare), axle(empty), ground(empty)]).

test_03 :- go([hand(flat), trunk(spare), axle(empty), ground(empty)],
            [hand(empty), trunk(spare), axle(empty), ground(flat)]).

test_04 :- go([hand(empty), trunk(spare), axle(empty), ground(flat)],
            [hand(flat), trunk(spare), axle(empty), ground(empty)]).

test_05 :- go([hand(spare), trunk(empty), axle(empty), ground(flat)],
            [hand(empty), trunk(empty), axle(spare), ground(flat)]).

test_06 :- go([hand(flat), trunk(empty), axle(spare), ground(empty)],
            [hand(empty), trunk(flat), axle(spare), ground(empty)]).

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

Другая причина, по которой результаты теста перечислены здесь, состоит в том, чтобы показать, что некоторые из шагов выбраны не так, как вы думаете или не предполагали, и не работают точно так, как вы ожидаете, но все же запрос к опубликованному вопрос работает как положено. Поэтому, если вы пишете тестовые примеры, и они возвращают что-то подобное, не думайте, что ваш move/3 недействителен или содержит ошибки, они могут не иметь. Когда вы получите все move/3 и последний запрос, работающий, как и ожидалось, вернитесь назад и попытайтесь понять, почему происходят эти множественные перемещения, а затем измените их, если хотите.

?- test_01.
moves are
take_from_trunk(spare)
true.

?- test_02.
moves are
take_from_trunk(spare)
place_on_ground(spare)
take_off_axle(flat)
place_in_trunk(flat)
pickup_from_ground(spare)
place_on_axle(spare)
take_from_trunk(flat)
place_on_ground(flat)
take_off_axle(spare)
place_in_trunk(spare)
pickup_from_ground(flat)
true.

?- test_03.
moves are
place_on_ground(flat)
true.

?- test_04.
moves are
take_from_trunk(spare)
place_on_axle(spare)
pickup_from_ground(flat)
place_in_trunk(flat)
take_off_axle(spare)
place_on_ground(spare)
take_from_trunk(flat)
place_on_axle(flat)
pickup_from_ground(spare)
place_in_trunk(spare)
take_off_axle(flat)
true.

?- test_05.
moves are
place_on_axle(spare)
true.

?- test_06.
moves are
place_on_ground(flat)
take_off_axle(spare)
place_in_trunk(spare)
pickup_from_ground(flat)
place_on_axle(flat)
take_from_trunk(spare)
place_on_ground(spare)
take_off_axle(flat)
place_in_trunk(flat)
pickup_from_ground(spare)
place_on_axle(spare)
true.
...